JAVA入门(一)——反射机制理解

本篇博客记录了最近学习的java反射相关知识,之前一直想入门java漏洞的,但是尝试几次都因为看不懂放弃了,这一次利用大佬们的博客借助chatGPT终于弄懂了反射机制。

反射概念

首先要懂基本的java语法,感受过java面向对象的特点,最好上手练习过java编程(就学校里面上课那种程度就可以,不需要太精通java)

正射

在理解反射之前要先了解什么是正射

public class Person{
	public int age;
    public int heighet;
    private int weight;
    public Person(){
        System.out.printf("初始化");
	}
    public void moveforward(){
        System.out.printf("向前移动了");
    }
    private void jump(){
        System.out.printf("跳起来了");
    }
}
public class Person_test{
    public static void main(String[] args){
        Person mike = new Person();
    }
}

上面Person mike = new Person();这个从类实例化为对象的过程就是正射,即知道类Person,要使用类Persion,使用new关键字进行创建对象就是正射。

反射

那什么是反射呢,就是当你动态调用类的时候,比如给用户三个选项分别对应三个类,在用户没有选择之前你是不知道到底要调用哪个类的,这时候就出现了反射机制,就是动态调用类,下面是gpt的理解

java为什么要用反射机制,反射机制有什么用


Java之所以引入反射机制,是为了在运行时动态地获取类的信息并操作类或对象。反射机制允许程序在运行时通过类名来获取类的属性、方法和构造函数等信息,以及在运行时调用对象的方法,而无需在编译时确定这些信息。

反射机制有以下几个主要用途:

1. 动态加载类:Java程序在运行时可以根据配置文件、用户输入或其他外部条件来加载不同的类,实现灵活的扩展性和动态性。

2. 运行时获取类信息:通过反射,可以在运行时获取类的属性、方法、父类、接口等信息,这对于实现框架和工具类非常有用。

3. 动态创建对象:可以使用反射动态地创建类的实例,例如根据用户输入的类名来实例化对象。

4. 动态调用方法:通过反射可以在运行时调用类的方法,这对于实现插件化、代理等功能非常有用。

5. 修改私有属性和方法的访问权限:反射可以绕过访问权限的限制,使得程序可以访问和修改类的私有属性和方法,但这样做可能会破坏封装性和安全性,因此应谨慎使用。

虽然反射机制为Java提供了很大的灵活性和动态性,但由于在运行时动态获取信息和调用方法会带来额外的开销,并且失去了编译时类型检查的优势,因此在性能要求高、对类型安全要求严格的场景中,应该避免过度使用反射。

可以看到其中比较有意思的是可以修改私有属性和方法的访问权限,我当时看到这里就有疑问

为什么要使用类来访问私有方法呢,我把类实例化为对象不就可以访问私有方法了

后来看到了GPT给出的代码明白了

public class Person {
    private int age;

    public Person(int age) {
        this.age = age;
    }

    private void sayHello() {
        System.out.println("Hello!");
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Person person = new Person(30);

        // 使用反射获取私有属性 age,并修改其值
        Field ageField = Person.class.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.setInt(person, 35);

        System.out.println("New age: " + person.age); // Output: New age: 35

        // 使用反射调用私有方法 sayHello
        Method sayHelloMethod = Person.class.getDeclaredMethod("sayHello");
        sayHelloMethod.setAccessible(true);
        sayHelloMethod.invoke(person); // Output: Hello!
    }
}

我们可能获取不到那个类,甚至于代码中都没有导入我们想用的类,所以才需要用反射的方法来获取类,进一步解释一下就是如果上面的Person类不在这里,我们应该怎么在我们能控制的类中使用Person类呢,正常来讲我们肯定是访问不了Person中的私有方法的,但是我们可以通过反射的方法来获取我们想要类的某个方法,就可以进行命令执行了

如何获取类呢

主要有三种方法

//1.使用class.forName()方法

Class p=Class.forName("test.Person"); //里面要填:类所在的包名+类名

//2.使用实例化对象的getClass()方法

Person p=new Person(); //实例化对象
Class p1=p.getClass();//通过实例化对象来获取完整类的原型
//P牛的介绍中说这种方法其实不叫反射了,但也是获取class的一种方法

//3.使用类的class方法

 Class p=phone.class;

如何通过获取的类实例化

有两种方法

//1.通过class的newInstance()方法

Class p=Class.forName("test.Person");
Object mike=p.newInstance();
//这里也有另一种写法,区别是要进行强制类型转化
Class p=Class.forName("test.phone");
phone mike=(phone)p.newInstance();

//2.通过constructor的newInstance()方法
//这里可能对构造器有点疑问,其实就是构造函数,不同点在第一种方法只能使用默认调用了无参数的构造函数,而下面这种是可以调用有参数的构造函数的
Class p=Class.forName("test.Person");
Constructor constructor=p.getConstructor();
Object mike=constructor.newInstance();//这里同上一样有另一种写法,就不再赘述

下面是结合GPT的一些学习过程

java中class的newInstance()方法有什么用为什么要这么做





在Java中,`newInstance()`方法是`java.lang.Class`类的一个方法,用于创建该类的实例(对象)。它是一种通过反射机制动态创建对象的方式。

使用`newInstance()`方法的主要目的是在运行时通过类的全限定名(Fully Qualified Name,FQN)来创建对象,而不是在编译时固定地使用`new`关键字直接创建对象。这样做有几个用途和优势:

1. 动态加载类:通过`newInstance()`,你可以根据类名的字符串动态地加载一个类,这在某些场景下非常有用,比如插件系统或者依赖注入框架,它们需要根据配置或用户输入动态选择类并实例化。

2. 灵活配置:有时候你可能需要根据配置文件或用户输入来创建不同的对象。使用`newInstance()`可以根据配置在运行时创建不同的对象,而无需编写大量的条件判断或者工厂类。

3. 反射:`newInstance()`是Java反射机制的一部分。反射允许在运行时检查和修改类的结构,并在运行时调用类的方法。`newInstance()`就是通过反射来实现动态创建对象的功能。

然而,需要注意的是,`newInstance()`有一些限制和弊端:

1. 可能抛出异常:`newInstance()`使用无参构造函数来创建对象,如果类没有无参构造函数或者无参构造函数不可访问(比如是私有的),则会抛出`InstantiationException`异常。

2. 性能开销:相较于直接使用`new`关键字,`newInstance()`是通过反射机制创建对象,这可能会带来一定的性能开销,因为反射在一定程度上比直接调用构造函数更为耗时。

3. 缺乏编译时检查:使用`newInstance()`创建对象是在运行时进行的,因此缺乏编译时的类型检查。这意味着如果类名在运行时出现错误或者不存在,只有在运行时才能发现这个问题。

总的来说,`newInstance()`方法是一种强大且灵活的功能,但在使用时需要谨慎处理,并且考虑到其性能和安全方面的影响。在大多数情况下,最好的实践是直接使用`new`关键字来创建对象,除非你有特殊的动态加载类或者配置类的需求。



我的理解就是使用Class p=Class.forName("test.Person");
Object mike=p.newInstance();  p1就是phone的对象,这样理解对吗




您的理解基本上是正确的。让我们一步步解释这个代码片段:

```java
Class p = Class.forName("test.phone");
```

这一行使用`Class.forName()`方法根据类的全限定名(FQN)字符串加载`test.phone`类的`Class`对象。这是一个重要的步骤,它将在运行时获取类的信息,包括成员变量、方法等。

```java
Object p1 = p.newInstance();
```

这一行通过在`p`的`Class`对象上调用`newInstance()`方法来创建一个`test.phone`类的实例(对象)。由于`newInstance()`方法返回`Object`类型,所以需要将其转换为实际的类类型。在这里,您可以将`p1`视为`test.phone`类的一个对象。

但是需要注意的是,自从Java 9版本开始,`newInstance()`方法已经被标记为过时(deprecated),并且在未来版本中可能会被移除。因此,更现代和推荐的方式是使用构造函数引用来实现相同的功能:

```java
Constructor<?> constructor = p.getConstructor(); // 获取无参构造函数
Object p1 = constructor.newInstance(); // 创建对象
```

这将更明确地表示您是在通过构造函数来创建对象,并且可以避免使用过时的方法。

总结来说,您的理解是对的,但是请注意`newInstance()`方法已经过时,使用构造函数引用是更好的替代方案。

基本已经理解实例化的部分了,然后就是最重要的访问和使用私有变量和私有方法了,也就是我们的最终目的

使用私有变量和私有方法

获取私有变量

Class p=Class.forName("test.Person");
 Field f=p.getDeclaredField("weight");

获取私有方法

Class p=Class.forName("test.Person");
Method m=p.getDeclaredMethod("jump");

最后基础知识了解完成之后,使用反射

public void execute(String className, String methodName) throws Exception {
 Class clazz = Class.forName(className);
 clazz.getMethod(methodName).invoke(clazz.newInstance());
}

将上面className和methodName换掉就是执行任意类的反射

要利用的类

学习中发现一般使用runtime类,其中有私有的无参构造器和exec方法,可以使用反射的方法调用

Class p=Class.*forName*("java.lang.Runtime");
Constructor constructor=p.getDeclaredConstructor(); //调用私有构造器
constructor.setAccessible(true); //修改作用域
Method m=p.getMethod("exec", String.class); //获取exec方法
Object o=constructor.newInstance(); //实例化对象
m.invoke(o,"calc"); //调用exec方法,执行calc命令

参考

p牛的java安全漫谈(反射篇)https://govuln.com/docs/java-things/

JAVA安全初探(一):从面向对象编程到Java反射JAVA安全初探(一):从面向对象编程到Java反射 - 先知社区 (aliyun.com)

posted @ 2023-07-25 11:18  无据  阅读(33)  评论(0编辑  收藏  举报