java反射机制

2年前 (2022) 程序员胖胖胖虎阿
352 0 0

java反射机制​​

​​java反射机制

一.概念

自己理解:java反射机制可通过类的全路径名对类进行动态加载实例化,获取类信息及类里面的成员变量,方法,改造方法等;加载完类之后,在堆中就产生了一个Clas类型的对象(一个类只有一个对象),这个对象包含了类的完整结构信息,通过这个对象可以得到类的结构(方法,构造方法,属性,结构)。
网上解释:
(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
(2)Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。

二.使用

1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时得到任意一个类所具有的成员变量和方法
4.在运行时调用任意一个对象的成员变量和方法
5.生成动态代理

 //1.使用Properties
        Properties properties = new Properties();
        properties.load(new BufferedInputStream(new FileInputStream("src/main/resources/test.properties")));
        String classPath = properties.get("classfullpath").toString();
        String methodName = properties.get("method").toString();
        System.out.println(classPath);
        System.out.println(methodName);

        //2.使用反射机制解决
        //(1)加载类,返回class类型的对象cls
        Class cls = Class.forName(classPath);
        //1.9之后不能用了,因为只能调用无参构造,没办法构
        Object objPerson1 =instanceObj.newInstance();
        //1.9之后采用这个来实例化对象
        //(2)通过cls得到你加载的类的对象实例
        Object o=cls.getDeclaredConstructor().newInstance();
        System.out.println("o的运行类型=" + o.getClass());
        //(3)通过cls得到你加载的类里面的方法
        //即:在反射中,可以把方法视为对象(万物皆对象)
        Method method1 = cls.getMethod(methodName);
        //(4)通过method1调用方法:即通过方法对象来实现调用方法
        method1.invoke(o);//传统方法,对象.方法(),反射机制:方法.invoke(对象)

        Field nameField= cls.getField("age");
        System.out.println("nameField"+nameField.get(o));
        Constructor constructors = cls.getConstructor();
        System.out.println("constructors"+constructors);

java反射机制

三.反射相关类

java.lang.refiect
java.lang.Class:代表一个类
java.lang.refiect.Method:代表类的方法
java.lang.refiect.Field:代表类的成员变量
java.lang.refiect.Constructors:代表类的构造方法

 //根据类路劲获取类信息
        Class clazz = Class.forName("com.example.content.test.Student");
        //实例化对象
        Object o = clazz.getConstructor().newInstance();
        //获取方法
        Method hi = clazz.getMethod("hi");
        //获取构造方法
        Constructor constructor = clazz.getConstructor();
        //获取属性(属性需要是public的才可以,不是会提示Exception in thread "main" java.lang.NoSuchFieldException)
        Field nameField = clazz.getField("name");
        System.out.println("获取方法="+hi);
        System.out.println("获取构造方法="+constructor);
        System.out.println("获取属性="+nameField);

结果:

获取方法=public void com.example.content.test.Student.hi()
获取构造方法=public com.example.content.test.Student()
获取属性=public java.lang.String com.example.content.test.Student.name

四.反射的优点和缺点

缺点:耗时,效率慢

 public static void main(String[] args) throws Exception {
        m1();
        m2();
        m3();
    }
    //创痛方法调用hi
    public static void m1() {
        Student student = new Student();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            student.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("传统方法耗时" + (end - start));
    }
    //创痛方法调用hi
    public static void m2() throws Exception {
        Class clazz = Class.forName("com.example.content.test.Student");
        Object cls = clazz.getConstructor().newInstance();
        Method method = clazz.getMethod("hi");
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            method.invoke(cls);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方法耗时" + (end - start));
    }
    //创痛方法调用hi
    public static void m3() throws Exception {
        Class clazz = Class.forName("com.example.content.test.Student");
        Object cls = clazz.getConstructor().newInstance();
        Method method = clazz.getMethod("hi");
        method.setAccessible(true);
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            method.invoke(cls);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射优化方法耗时" + (end - start));
    }

输出:

传统方法耗时3
反射方法耗时52
反射优化方法耗时20

反射调用优化-关闭访问监测(能提升一些,一定程度上优化性能)
具体方法:AccessibleObject
Method method = clazz.getMethod("hi"); method.setAccessible(true);

五.Class类分析

java反射机制
1.class也是类,继承object类
2.创建类
(1)传统方式:
/**
* public Class<?> loadClass(String name) throws ClassNotFoundException {
* return loadClass(name, false);
* }
*/
Student student=new Student();
(2)java反射:
Class aClass = Class.forName(“com.example.content.test.Student”);
对于某个类的class类对象,在内存中只存在一份,因此类只加载一次
(3)每个类的实例都知道他是由哪个class实例所生成
(4)通过class对象可以完整地得到一个类的完整结构,通过一系列api
(5)class对象放在堆空间
(6)类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法名,变量等等)

五.Class类的常用方法

 //通过类路径获取类
        Class aClass = Class.forName("com.example.content.test.Student");
        System.out.println(aClass);//显示cla对象,是哪个类的Class对象
        System.out.println(aClass.getClass());//运行类型
        //得到包名
        Package aPackage = aClass.getPackage();
        System.out.println(aPackage);
        //得到全类名
        String className = aClass.getName();
        System.out.println(className);
        //创建对象实例
        Student student = (Student) aClass.getConstructor().newInstance();
        System.out.println(student);
        //得到属性
        Field nameField = aClass.getField("name");
        System.out.println(nameField.get(student));
        //设置属性
        nameField.set(student, "萌萌猫");
        System.out.println(nameField.get(student));
        //得到所有属性
        Field[] fields = aClass.getFields();
        System.out.println(fields);

六.获取类对象的几种方式:

(1)Class aClass = Class.forName(“com.example.content.test.Student”);
前提:已知类的全路径,且该类在类路径下,通过此方法获取
应用场景:多用于配置文件,读取类全路径,加载类
(2) Class aClass1 = Student.class();
前提:已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高
应用场景:多用于参数传递,比如通过反射得到对应构造器对象
(3) Class aClass1 = student.getClass();

    //通过类文件
        Class aClass = Class.forName("com.example.content.test.Student");
        //类名.class
        Class student=Student.class;
        //类名.class
        Student student1=new Student();
        Class aClass1=student1.getClass();
        //类加载器
        ClassLoader classLoader = student.getClassLoader();//得到类加载器
        Class<?> aClass2 = classLoader.loadClass("com.example.content.test.Student");//通过类加载器得到class对象
        System.out.println("aClass="+aClass);
        System.out.println("student="+student);
        System.out.println("aClass1="+aClass1);
        System.out.println("aClass2="+aClass2);

运行结果:

aClass=class com.example.content.test.Student
student=class com.example.content.test.Student
aClass1=class com.example.content.test.Student
aClass2=class com.example.content.test.Student

六.Java中类变量(静态变量)和实例变量区别

成员变量:把类内、方法体外定义的变量称为成员变量。
Java中的成员变量分为两种:
一是没有static修饰的,这些成员变量是对象中的成员,称为实例变量。
二是有static修饰的,称为类变量(静态变量)。
类变量和实例变量的区别是:
类变量在内存中只存一份,在程序运行时,系统只为类变量分配一次内存,只进行一次的初始化。在加载类的过程中完成类变量的内存分配。
类变量可以通过类名访问。
实例变量是属于对象中的成员,每创建一个类的对象,就会为实例变量分配一次内存,实例变量在内存中有多个拷贝,分别属于不同对象,他们之间互不影响。
示例:


public class scope {
	static int a;
	int b;
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		a++;
		scope s1 = new scope();
		s1.a++;
		s1.b++;
		scope s2 = new scope();
		s2.a++;
		s2.b++;
		scope.a++;
		System.out.println("a="+a);
		System.out.println("s1.a="+s1.a);
		System.out.println("s2.a="+s2.a);
		System.out.println("s1.b="+s1.b);
		System.out.println("s2.b="+s2.b);
	}
}

运行结果为:
a=4
s1.a=4
s2.a=4
s1.b=1
s2.b=1
a是类变量,b是实例变量。a在内存中只有一份,a可以通过类名访问,也可以通过对象名访问。无论哪种访问形式都是对同一个a进行操作,所以连续加1后,a的值为4.
而对于b,每次创建一个对象,内存中就有一份b的空间,在这里对象s1和s2中分别有一个变量b,对他们操作需要通过不同的对象名,所以对他们的操作互不影响。在分别加1后,都为1。

七.类加载:

(1) 加载过程:加载-连接(验证,准备,解析)-初始化
验证:(验证代码是否jvm的规范,文件格式是否正确,元数据验证,字节码验证,符号引用验证)
准备(为类中定义的变量,默认初始化并分配空间,即静态变量分配内存并设置变量的初始值,通常情况为该数据类型的“零值”。)
代码示例:

class a{
    //属性-成员变量-字段 类加载的链接阶段-准备 属性是如何处理
    //1.a是实例尚需经,不是静态变量,因此在准备阶段,不会分配内存
    //2.b是静态变量,在此会分配内存并赋该数据类型上午默认初始化值0,而不是1
    //3.c是static final 是常量,是不可改变的,一旦赋值就不会发生变化,此处赋值1
    public int a=1;
    //会为该属性分配内存,并赋初始化值0,1放在初始化中
    public static int b=1;  //b=0
    public final static int c=1;// c=1
}

解析(解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,此过程伴随着第二阶段验证中符号引用验证,放到jre中,类处于可运行的状态);
初始化(真正执行类中定义的java程序代码)
代码示例:

java反射机制
java反射机制
(2)类加载的两种形式
静态加载:依赖性太强,编译时加载相关的类,如果没有则报错
动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,依赖性不强
应用实例:

 public static void main(String[] args) throws Exception {
        Scanner scanner=new Scanner(System.in);
        String key = scanner.next();
        switch (key){
            case "1":
                Student student=new Student();//静态加载(该类必须存在,不然此处声明时会报错,无法成功编译)
                student.hi();
                break;
            case "2":
                //动态加载(当程序执行到这里的时候才会去检测并编译,不会影响编译结果)
                Class aClass = Class.forName("com.example.content.test.app");
                Object o = aClass.getConstructor().newInstance();
                Method method = aClass.getMethod("hi");
                method.invoke(o);
            default:
                System.out.println("************");

        }
    }

运行结果:

2
Exception in thread "main" java.lang.ClassNotFoundException: com.example.content.test.app
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:315)
	at com.example.content.test.Refiection06.main(Refiection06.java:27)
1

Process finished with exit code 0

总结:
动态加载:程序在运行时调用相应方法,即使其他方法是错误的,程序依旧会执行。通过动态加载可以让程序的可延长行大大提升,对以后的维护和扩展有重要意义。
静态加载:程序在编译时执行。在执行过程中加载所有可能执行到的程序。在这种加载方式下,只要加载中一个方法出错,程序就不能运行。我们一般写程序默认的是静态加载。

八.通过反射创建对象

1.方式一:调用类中的public修饰的无参构造器
2.方式二:调用类中的指定构造器

public class Refiection09 {
    public static void main(String[] args) throws Exception {
        //1.先获取user类的class对象
        Class aClass = Class.forName("com.example.content.test.user");
        //2.通过public的无参构造器创建实例
        Object o = aClass.getConstructor().newInstance();
        System.out.println(o);
        //3.通过public的有参构造器创建实例
        Object o1 = aClass.getConstructor(String.class).newInstance("李四");
        System.out.println(o1);
        //4.通过非public的有参构造器创建实例
       /* Exception in thread "main" java.lang.NoSuchMethodException: com.example.content.test.user.<init>(int, java.lang.String)*/
        //Object o3 = aClass.getConstructor(int.class, String.class).newInstance(5, "王麻子");由于该方法是私有构造方法,会报上面的异常
        Constructor declaredConstructor = aClass.getDeclaredConstructor(int.class, String.class);//他可以获取所有构造方法
        System.out.println(declaredConstructor);
        declaredConstructor.setAccessible(true);//暴破【暴力破解】,使用反射可以访问private构造器
        Object o3 = declaredConstructor.newInstance(5, "王麻子");
        System.out.println(o3);
    }
}

class user {
    private int age = 1;
    private String name = "张三";

    public user() {

    }

    public user(String name) {
        this.name = name;
    }

    private user(int age, String name) {
        this.age = age;
        tuser[age=1,name=张三}
user[age=1,name=李四}
private com.example.content.test.user(int,java.lang.String)
user[age=5,name=王麻子}his.name = name;
    }

    public String toString() {
        return "user[age=" + age + ",name=" + name + "}";
    }


}

运行结果:

user[age=1,name=张三}
user[age=1,name=李四}
private com.example.content.test.empoless(int,java.lang.String)
user[age=5,name=王麻子}

九.通过反射操作属性和方法

代码举例:

public class Refiection10 {
    public static void main(String[] args) throws Exception {
        //1.先获取user类的class对象
        Class aClass = Class.forName("com.example.content.test.User");
        //2.通过public的无参构造器创建实例
        Object o = aClass.getDeclaredConstructor().newInstance();
        System.out.println(o);
      /*  Exception in thread "main" java.lang.NoSuchFieldException: age
        at java.base/java.lang.Class.getField(Class.java:1999)
        at com.example.content.test.Refiection10.main(Refiection10.java:19)*/
//        Field age = aClass.getField("age");
//        注意:当属性为私有的时候,需要暴破,如果只是普通属性,可以直接getField
        Field age = aClass.getDeclaredField("age");
        age.setAccessible(true);//获取私有属性字段之后还需要进行暴力破解,设置取消访问检查
        age.set(o, 15);
        Field name = aClass.getDeclaredField("name");
        name.setAccessible(true);
        name.set(o, "小明");
        /*Exception in thread "main" java.lang.NoSuchMethodException: com.example.content.test.User.a()
        at java.base/java.lang.Class.getMethod(Class.java:2108)
        at com.example.content.test.Refiection10.main(Refiection10.java:34)*/
        Method a = aClass.getMethod("a", int.class);//此处需要指定参数类型
        a.invoke(o, 11);
        Method b = aClass.getDeclaredMethod("b", int.class);//此处需要指定参数类型
        b.setAccessible(true);
        b.invoke(o, 22);
        Method c = aClass.getDeclaredMethod("c", int.class);//此处需要指定参数类型
        c.setAccessible(true);
        //静态方法的两种调用方式
        c.invoke(o,33);
        c.invoke(null,33);
        System.out.println(o);
    }
}

class User {
    private int age;
    private String name;

    public User() {

    }

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

    public User(String name) {
        this.name = name;
    }

    private User(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public String a(int a) {
        System.out.println("调用公共方法" + a);
        return "调用公共方法" + a;
    }

    private String b(int b) {
        System.out.println("调用公共方法" + b);
        return "调用私有方法" + b;
    }

    private static String c(int c) {
        System.out.println("调用公共方法" + c);
        return "调用私有静态方法" + c;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

运行结果:

User{age=0, name='null'}
调用公共方法11
调用公共方法22
调用公共方法33
调用公共方法33
User{age=15, name='小明'}

九.课后练习

java反射机制
代码:

public class TestRefiect {


    public static void main(String[] args) throws Exception {
        //1.获取类路径
        Class<?> aClass = Class.forName("com.example.content.test.PrivateTest");
        //2.运行实例化
        Object o = aClass.getDeclaredConstructor().newInstance();
        //3.获取属性
        Field name = aClass.getDeclaredField("name");
        name.setAccessible(true);//需要暴破
        //4.获取方法
        Method nameMethod = aClass.getMethod("getName");
        //执行方法
        nameMethod.invoke(o);
        //4.获取属性值
        System.out.println(name.get(o));
        //5.设置值
        name.set(o, "kitty");
        nameMethod.invoke(o);
        System.out.println(name.get(o));
    }
}

class PrivateTest {
    private String name = "hellokitty";
    public String getName() {
        System.out.println("方法执行打印=:" + name);
        return name;
    }
}

运行结果:

方法执行打印=:hellokitty
hellokitty
方法执行打印=:kitty
kitty

java反射机制

package com.example.demo.test;/**
 * @Auther: 12855
 * @Date: 2022/3/21 14:30
 * @Description:
 */

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * @Auther: 12855
 * @Date: 2022/3/21 14:30
 * @Description:
 */
public class Study {
    public static void main(String[] args) throws Exception {
        Class aClass = Class.forName("java.io.File");
        Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
        for (int i = 0; i < declaredConstructors.length; i++) {
            System.out.println("declaredConstructor=" + declaredConstructors[i]);
        }
        //获取实例对象
        //得到指定的构造器
        Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class);
        //设置参数
        String path = "d:\\aa.txt";
        //获取实例对象
        Object o = declaredConstructor.newInstance(path);
        //找出对应方法
        Method createNewFile = aClass.getMethod("createNewFile");
        //执行方法
        createNewFile.invoke(o);
        File file = new File("d:\\bb.txt");
        file.createNewFile();

    }
}

运行结果:

declaredConstructor=public java.io.File(java.lang.String)
declaredConstructor=public java.io.File(java.lang.String,java.lang.String)
declaredConstructor=public java.io.File(java.io.File,java.lang.String)
declaredConstructor=public java.io.File(java.net.URI)
declaredConstructor=private java.io.File(java.lang.String,java.io.File)
declaredConstructor=private java.io.File(java.lang.String,int)

九.注解

元注解:@Target,Retention,@Document,@Inherited
@Target() :描述注解的使用范围
@Retention:就是我们需要告诉编译器我们需要在什么级别保存该注释信息,用于描述注解的生命周期。
@Document:
@Inherited
常用的内置注解:
@Override:修辞方法,表示打算重写超类中的方法声明。
@Deprecated:这个注释可以修辞方法,属性,类,表示不鼓励程序员使用这样的元素,通常是因为他很危险或有更好的选择。
@SuperWarnings:这个注解主要是用来抑制警告信息的,我们在写程序时,可能会报很多黄线的警告,但是不影响运行,我们就可以用这个注解来抑制隐藏它。与前俩个注解不同的是我们必须给注解参数才能正确使用他。

版权声明:程序员胖胖胖虎阿 发表于 2022年9月20日 上午9:48。
转载请注明:java反射机制 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...