通过Java语言中的反射机制可以操作字节码文件(读和修改字节码文件),操作代码片段。
java.lang.Class:代表整个字节码,整个类。
java.lang.reflect.Method:代表字节码中的方法字节码,代表类中的方法。
java.lang.reflect.Constructor:代表字节码中的构造方法字节码,代表类中的构造方法。
java.lang.reflect.Field:代码字节码中的属性字节码,代表类中的成员属性。
示例:
//java.lang.Class
public class ReflectTest {
//java.lang.reflect.Field
public int age;
//java.lang.reflect.Constructor
public ReflectTest(){
}
//java.lang.reflect.Method
public void doSome(){
}
}
要想要操作一个类的字节码,首先要获取到这个类的字节码。
示例:
public class ReflectTest {
public static void main(String[] args) {
try {
Class c1 = Class.forName("java.lang.String");// c1表示String.class文件,或者说是String类
Class c2 = Class.forName("java.lang.Runnable");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
java中任何一个对象都有getClass()方法
示例:
public class ReflectTest {
public static void main(String[] args) {
Class c1 = null;
try {
c1 = Class.forName("java.lang.String");// c1表示String.class文件,或者说是String类
Class c2 = Class.forName("java.lang.Runnable");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 通过getClass()方法获取字节码文件
String x = "test";
Class c3 = x.getClass();
System.out.println(c1 == c3); // 输出true
}
}
java语言中任何一种类型,包括基本数据类型,都有class属性
示例:
public class ReflectTest {
public static void main(String[] args) {
String x = "test";
Class c3 = x.getClass();
// java语言中所有类型都有.class属性
Class c4 = String.class;
System.out.println(c4 == c3); //输出true
}
}
获取到Class文件后能干什么?
通过Class的newInstacne方法实例化对象,newInstance实际上是调用了无参构造方法,必须保证无参构造方法存在。
示例:
User测试类
public class User {
public User() {
System.out.println("无参构造方法被调用!!!");
}
}
通过获取User类的字节码文件,实例化User类对象
public class ReflectTest01 {
public static void main(String[] args) {
try {
// 获取字节码文件
Class c1 = Class.forName("yxl.more.User");
// 操作字节码文件
// 创建User对象,newInstance会调用User类中的无参构造方法,完成对象的创建
// 如果没有无参构造方法,会报错。
Object user = c1.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
使用反射机制创建对象更加灵活
不需要修改源代码,只需要修稿配置文件,就能够实现不同对象的实例化。大部分高级框架底层的实现原理都采用了反射机制。
classinfo.properties 配置文件内容
className=yxl.more.User
测试类内容
public class IoProperties {
public static void main(String[] args) {
try {
// 读取文件
FileReader reader = new FileReader("D:\\JAVA笔记\\Test\\AnnotationTest\\src\\classinfo");
Properties properties = new Properties();
// 加载流
properties.load(reader);
// 关闭流
reader.close();
// 获取Map键值对对象,得到key为className的值
String name = properties.getProperty("className");
// 获取class文件
Class c1 = Class.forName(name);
// 实例化对象
System.out.println(c1.newInstance());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
Class.forName()的执行会导致:类加载。如果只想让类中的静态代码块执行,可以使用Class.forName(“完整类名”),因为静态代码块会在类加载时执行。
示例验证:
public class ReflectTest02 {
public static void main(String[] args) {
try {
Class.forName("yxl.reflect.User");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
class User{
// 静态代码块 会在类加载时执行
static {
System.out.println("静态代码块执行!!!");
}
}
**类路径下:**凡是在src下的都是类路径下。
src是类的根路径。
使用此方法的前提是这个文件在类路径下。
代码解释:
Thread.currentThread():获取当前线程对象
getContextClassLoader():获取当前线程的类加载器对象
getResource():获取资源 (当前线程的类加载器会从类的根路径开始加载资源,所以需要传入文件的从src之后的路径名)
public class GetPath {
public static void main(String[] args) {
String path = Thread.currentThread()
.getContextClassLoader()
.getResource("test")
.getPath();
System.out.println(path);
}
}
不再是返回绝对路径了。
InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo");
public class IoProperties {
public static void main(String[] args) {
try {
// FileReader reader = new FileReader("D:\\JAVA笔记\\Test\\AnnotationTest\\src\\classinfo");
// 以流的形式返回
InputStream reader = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream("classinfo");
Properties properties = new Properties();
properties.load(reader);
reader.close();
String name = properties.getProperty("className");
Class c1 = Class.forName(name);
System.out.println(c1.newInstance());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
便于获取配置文件中的内容,简化了创建流、加载流、关闭流等一系列操作。
使用条件:
被绑定的资源必须是properties文件,且这个文件必须在类路径下,同时在写路径时,properties的文件扩展名不能写。
使用步骤:
示例代码:
public class ResourceBundleTest {
public static void main(String[] args) {
ResourceBundle bundle = ResourceBundle.getBundle("classinfo");
System.out.println(bundle.getString("className"));
}
}