JavaSE基础:反射机制
文章目录
前言
以下知识点讲解反射机制
一、基础概念
1、反射机制的定义
通过java语言中的反射机制可以操作字节码文件。
优点类似于黑客。(可以读和修改字节码文件。)
通过反射机制可以操作代码片段。(class文件。)
可以让程序更加灵活。
2、反射机制相关的类在哪个包下?
java.lang.reflect.*;
3、反射机制相关的主要的类
java.lang.Class
java.lang.reflect.Method;
java.lang.reflect.Constructor;
java.lang.reflect.Field;
4、在java中获取Class的三种方式
第一种:
Class c = Class.forName("完整类名");
第二种:
Class c = 对象.getClass();
第三种:
Class c = int.class;
Class c = String.class;
5、获取了Class之后,可以调用无参数构造方法来实例化对象
//c代表的就是日期Date类型
Class c = Class.forName("java.util.Date");
//实例化一个Date日期类型的对象
Object obj = c.newInstance();
一定要注意:
newInstance()底层调用的是该类型的无参数构造方法。
如果没有这个无参数构造方法会出现"实例化"异常。
6、如果只想让一个类的“静态代码块”执行的话,可以怎么做?
Class.forName("该类的类名");
这样类就加载,类加载的时候,静态代码块执行!!!!
在这里,对该方法的返回值不感兴趣,主要是为了使用“类加载”这个动作。
二、基础练习例子
1、获取Class的三种方式
/*
反射机制:可以操作字节码文件
作用:可以让程序更加灵活。
java.lang.reflect.*;
java.lang.Class
java.lang.reflect.Method;
java.lang.reflect.Constructor;
java.lang.reflect.Field;
在java中获取Class的三种方式?
第一种:
Class c = Class.forName("完整类名");
第二种:
Class c = 对象.getClass();
第三种:
Class c = int.class;
Class c = String.class;
*/
import java.util.Date;
public class ReflectTest01 {
public static void main(String[] args) throws Exception{
// 第一种方式:Class c = Class.forname("完整类名")
Class c1 = Class.forName("com.lin.ReflectBasic.Basic.User");
Class c2 = Class.forName("java.lang.String");
Class c3 = Class.forName("java.util.Date");
// 第二种方式:【属于Object中的一个方法,每个对象都有的】
// Class c = 对象.getClass();
String s = "abc";
Class x = s.getClass();
// 判断c2和x内存地址是否相同
System.out.println(c2 == x); // true
Date time = new Date();
Class y = time.getClass();
//判断c3和y内存地址是否相同
System.out.println(c3 == y); // true
// 第三种方式:java中每个数据类型中能调用.class
Class z = String.class;
// 判断c2和z的内存地址是否相同
System.out.println(z == c2); // true
}
}
2、使用反射机制实例化对象
/*
获取了Class之后,可以调用无参数构造方法来实例化对象
//c代表的就是日期Date类型
Class c = Class.forName("java.util.Date");
//实例化一个Date日期类型的对象
Object obj = c.newInstance();
一定要注意:
newInstance()底层调用的是该类型的无参数构造方法。
如果没有这个无参数构造方法会出现"实例化"异常。
*/
public class ReflectTest02 {
public static void main(String[] args) {
// 已过时的方法 newInstance()
try {
Class c = Class.forName("com.lin.ReflectBasic.Basic.User");
// 实例化
Object obj = c.newInstance();
System.out.println(obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
User user = new User();
System.out.println(user);
}
}
3、验证反射机制的灵活性
提前准备的一个类
/*提前准备的一个类*/
public class User {
// Field
private String name;
private int no;
// Constructor
public User() {
System.out.println("无参构造方法调用!");
}
public User(String name, int no) {
this.name = name;
this.no = no;
}
// Method
public void dosome(){
System.out.println("dosome");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
}
提前准备的配置文件
classname = com.lin.ReflectBasic.Basic.User
/*
验证反射机制的灵活性
在不改变java代码源代码的基础之上,可以做到不同对象的实例化
非常灵活(符合OCP开闭原则:对外扩展开放,对修改关闭)
后期学习的高级框架工作中要使用
包括 SSH SSM
Spring SpringMVC MyBatis
Spring Struts Hibernate
...
这些框架底层的实现原理都采用了反射机制
*/
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;
public class ReflectTest03 {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {
// 通过IO流读取classinfo.properties文件
FileReader fileReader = new FileReader("Java_Reflect/classinfo.properties");
// 创建属性类对象Map
Properties properties = new Properties();
// 加载
properties.load(fileReader);
// 关闭流
fileReader.close();
// 通过key获取value
String classname = properties.getProperty("classname");
// 实例化对象
Class c = Class.forName(classname);
Object object = c.newInstance();
System.out.println(object);
}
}
4、关于路径问题
/*
关于路径名问题
String path = Thread.currentThread().getContextClassLoader()
.getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();
String path = Thread.currentThread().getContextClassLoader()
.getResource("abc").getPath(); //必须保证src下有abc文件。
String path = Thread.currentThread().getContextClassLoader()
.getResource("a/db").getPath(); //必须保证src下有a目录,a目录下有db文件。
String path = Thread.currentThread().getContextClassLoader()
.getResource("com/bjpowernode/test.properties").getPath();
//必须保证src下有com目录,com目录下有bjpowernode目录。
//bjpowernode目录下有test.properties文件。
这种方式是为了获取一个文件的绝对路径。(通用方式,不会受到环境移植的影响。)
但是该文件要求放在类路径下,换句话说:也就是放到src下面。
src下是类的根路径。
直接以流的形式返回:
InputStream in = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("com/bjpowernode/test.properties");
*/
import java.io.FileReader;
import java.util.Properties;
public class AboutPath {
public static void main(String[] args) throws Exception {
// 使用IO流classinfo.properties文件
// 使用这种方法的路径缺点:移植性差,在IDEA中默认的当前路径是project的根
// 这个方法离开了IDEA换到了其他位置可能当前路径就不是project的根
//FileReader fileReader = new FileReader("");
// 通用的方法:即使代码换了位置【到了linux系统等】编写仍然是通用的
// 使用此方法的前提是这类必须在根路径下
// 什么是类路径下?即src是类的根路径
String path = Thread.currentThread().getContextClassLoader()
.getResource("classinfo2.properties").getPath();
FileReader fileReader = new FileReader(path);
Properties properties = new Properties();
properties.load(fileReader);
fileReader.close();
String className = properties.getProperty("classname");
System.out.println(className);
}
}
5、资源绑定器(ResourceBundle)
提前准备的配置文件
classname=com.lin.ReflectBasic.Basic.User
/*
资源绑定器(java.util.ResourceBundle)
public abstract class ResourceBundle
extends Object
便于获取配置文件中的内容
只能绑定xxx.properties文件,并且这个文件必须在类路径下,文件的扩展名也必须是.properties
并且在写路径的时候,路径后面的扩展名不能写。
static ResourceBundle getBundle(String baseName)
使用指定的基本名称,默认语言环境和调用者的类加载器获取资源包。
*/
import java.util.ResourceBundle;
public class ResourceBundleTest {
public static void main(String[] args) {
// 获取calssinfo2资源包
ResourceBundle resourceBundle = ResourceBundle.getBundle("classinfo2");
String className = resourceBundle.getString("classname");
System.out.println(className);
}
}
6、安全性
在处理反射时安全性是一个较复杂的问题。反射经常由框架型代码使用,由于这一点,我们可
能希望框架能够全面介入代码,无需考虑常规的介入限制。但是,在其它情况下,不受控制的
介入会带来严重的安全性风险,例如当代码在不值得信任的代码共享的环境中运行时。
7、反射缺点
反射是一种强大的工具,但也存在一些不足。
性能问题。使用反射基本上是一种解释操作,我们可以告诉 JVM,我们希望做什么并且它满足我们的要求。用于字段和方法接入时反射要远慢于直接代码。性能问题的程度取决于程序中是如何使用反射的。如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。
使用反射会模糊程序内部实际要发生的事情。程序人员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术会带来维护问题。反射代码比相应的直接代码更复杂。
解决这些问题的最佳方案是保守地使用反射——仅在它可以真正增加灵活性的地方——记录其在目标类中的使用。