欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java反射基础知识整理

程序员文章站 2022-04-16 16:33:25
1、反射机制的作用通过java语言中的反射机制可以操作字节码文件,通过反射机制可以操作代码片段(class文件)反射机制相关类java.lang.reflectjava.lang,Class:代表整个字节符,代表一个类型java.lang.reflect.Method:代表字节码中的方法字节码java.lang.reflect.Constructor:代表字节码中的构造方法字节码java.lang.reflect.Field:代表字节码中的属性字节码2、获取一个类.....

目录

1、反射机制的作用

2、获取一个类的实例

3、使用Class.forName()方法加载类的静态代码块

4、获取配置文件的路径

5、java反编译

5.1、获取类中的成员变量

5.2、通过类名反编译出类的信息

5.3、使用反射机制去访问对象属性

5.4、使用反射获取类中的方法

5.5、通过反射机制调用类中的方法

5.6、通过反射调用构造方法

5.7、使用反射获取类的父类和父接口


1、反射机制的作用

通过java语言中的反射机制可以操作字节码文件,通过反射机制可以操作代码片段(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;

package Reflect;

/*
在java中获取Class的三种方式
 */
public class ReflectTest01_1 {
    public static void main(String[] args) throws ClassNotFoundException {
        String ss = new String("abc");
        Class c1 = Class.forName("java.lang.String");  //使用Class.forName()获取类
        Class c2 = ss.getClass();  //使用getClass()获取对象的Class
        Class c3 = String.class;   //使用class获取类
        System.out.println("使用Class.forName()获取类:" + c1);
        System.out.println("使用getClass()获取对象的Class:" + c2);
        System.out.println("使用class获取类:" + c3);
    }
}

java反射基础知识整理

 

2、获取一个类的实例

要操作一个类的字节码,首先要获取这个类的字节码。

获取类的实例有三种方式

方式一、使用Class.forName获取类的实例

package Reflect;

/*
要操作一个类的字节码,需要首先获取到这个类的字节码
 */
public class ReflectTest01 {
    public static void main(String[] args) throws ClassNotFoundException {
        /*
        Class.forName()
        1、静态方法
        2、方法的参数是一个字符串
        3、字符串需要的是一个完整的类名
        4、完整的类名必须带有包名,java.lang包也不能省略
         */
        Class c1 = Class.forName("java.lang.String");  //获取String类的一个实例
        Class c2 = Class.forName("java.util.Date");  //获取Date类的一个实例
        Class c3 = Class.forName("java.lang.Integer");  //获取Integer类的一个实例
        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);

        String s = new String("abc");
        Class x = s.getClass();
        System.out.println(c1 == x);  // "=="比较的是地址
    }
}

java反射基础知识整理

方式二、通过反射机制获取Class,通过Class来实例化对象

User类

package Reflect;

public class User {
    public User() {
    }
}

主方法

package Reflect;

public class ReflectTest02 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        //通过反射机制,获取Class,通过Class来实例化对象
        Class c = Class.forName("Reflect.User");
        //newInstance方法会调用User类的无参数构造方法,完成对象的创建
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

注:newInstance()底层调用的是该类的无参数构造方法。如果没有这个无参数构造方法会出现异常

Class类中的newInstance()方法

public T newInstance()
        throws InstantiationException, IllegalAccessException

 java反射基础知识整理

方式三、通过配置文件配置类,来获取类的实例

配置文件:classinfo.properties

java反射基础知识整理

package Reflect;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;
import java.util.Properties;

public class ReflectTest03 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {

        //这种方式代码就写死了。只能创建一个Date类型的对象
        Date d=new Date();

        //通过IO流读取classinfo.properties文件   classinfo.properties  D:\学习资料\java\java\classinfo.properties
        FileReader reader=new FileReader("D:\\学习资料\\java\\java\\classinfo.properties");
        //创建属性类对象Map
        Properties pro=new Properties();  //key value都是String
        //加载
        pro.load(reader);
        //关闭流
        reader.close();

        //通过key获取value
        String className=pro.getProperty("className");
        System.out.println(className);

        //通过反射机制实例化对象
        Class c=Class.forName(className);
        Object obj=c.newInstance();   //c.new Instance()会获取对象c的构造方法:public Date() {this(System.currentTimeMillis());返回当前时间 }
        System.out.println(obj);
    }
}

java反射基础知识整理

注:许多高级框架(ssm,SpringMVC,Springboot.....都采用了反射机制)

 

3、使用Class.forName()方法加载类的静态代码块

package Reflect;

public class ReflectTest04 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("Reflect.MyClass");  //只加载 MyClass类的静态代码块
    }
}

class MyClass {
    public static void main(String[] args) {
        System.out.println("MyClass类非静态代码块执行了");   //不被加载
    }

    static {
        System.out.println("MyClass类静态代码块执行了");
    }
}

java反射基础知识整理

 

延伸:在JDBC技术中也会使用到Class.forName()方法加载数据库驱动类

例:在数据库连接类中使用Class.forName()方法加载sqljdbc.jar包中的SQLServerDriver类的静态代码块

数据库连接类

private static String driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
	private static String url = "jdbc:sqlserver://localhost:1433;databaseName = CivilAviationTicketSystem",
			user = "AAA", password = "123456";
	private static Connection conn = null;
	static {
		try {
			Class.forName(driver); // 加载SQLServerDriver类中的静态代码块
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

SQLServerDriver类

static
  {
    try
    {
      DriverManager.registerDriver(new SQLServerDriver());
    }
    catch (SQLException localSQLException) {
      localSQLException.printStackTrace();
    }
  }

4、获取配置文件的路径

package Reflect;

/*
获取文件路径
可以通过以下方式获取文件的绝对路径,但前提是文件必须在类路径下(文件放在src目录下)
 */
public class Path {
    public static void main(String[] args) {
        /*
        Thread.currentThread():当前线程对象
        getContextClassloader():获取当前线程的类加载器对象
        getResource():【获取资源】这是类加载数据对象的方法
         */
        String path = Thread.currentThread().getContextClassLoader().
                getResource("classinfo2.properties").getPath();  //!!! 获取的文件一定要放在类路径下(src路径下)
        System.out.println(path);
    }
}

java反射基础知识整理java反射基础知识整理

 

5、java反编译

5.1、获取类中的成员变量

getFields()和 getDeclaredFields()方法
package Reflect;

import java.lang.reflect.Field;

public class ReflectTest05 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取整个类
        Class studentClass = Class.forName("Reflect.Student");

        Field[] fields = studentClass.getFields(); //getFields()只能获取类中的public修饰的Field
        Field[] fields_all = studentClass.getDeclaredFields();   //getDeclaredFields()可以获取类中所有的Field
        System.out.println("getFields方法获取的field个数:" + fields.length);
        System.out.println("getDeclaredFields方法获取的field个数:" + fields_all.length);
        //取出这个Field
        Field f = fields[0];
        //取出这个field它的名字
        String fieldName = f.getName();
        System.out.println("getFields方法获取的field:" + fieldName);
        System.out.print("getDeclaredFields方法获取的field:");
        for (Field field : fields_all) {
            System.out.print(field.getName() + ",");
        }
    }
}

 

5.2、通过类名反编译出类的信息

获取java.lang.String类中的成员变量信息

package Reflect;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReflectTest06 {
    public static void main(String[] args) throws ClassNotFoundException {
        //创建这个是为了拼接字符串
        StringBuilder s = new StringBuilder();
        Class studentClass = Class.forName("java.lang.String");
        //获取public class Student {
        s.append(Modifier.toString(studentClass.getModifiers()) + "class" + studentClass.getSimpleName() + "{\n");
        Field[] fields = studentClass.getDeclaredFields();
        //拿类的成员变量
        for (Field field : fields) {
            s.append("\t");   //修饰符前的缩进(制表符)
            s.append(Modifier.toString(field.getModifiers()));  //修饰符
            s.append(" ");  //修饰符后的空格
            s.append(field.getType().getSimpleName());    //获取类型(getType为获取类型的复杂名字),getSimpleName为获取类型的简单名
            s.append(field.getName());  //获取成员变量
            s.append(";\n");
        }
        s.append("}");
        System.out.println(s);
    }
}

java反射基础知识整理

5.3、使用反射机制去访问对象属性

Student类

package Reflect;

public class Student {
    public int no;
    private String name;
    protected int age;
    boolean sex;
}
package Reflect;

import java.lang.reflect.Field;

/*
使用反射机制去访问一个对象的属性
 */
public class ReflectTest07 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class studentClass = Class.forName("Reflect.Student");
        Object obj = studentClass.newInstance();  //obj是Student的对象(底层调用无参数构造方法)
        //获取no属性(根据属性的名称来获取Field)
        Field noField = studentClass.getDeclaredField("no");

        //给obj对象(Student对象)的no属性赋值
        noField.set(obj, 2020);

        //读取属性的值
        //两个要素:获取obj对象的no属性的值
        System.out.println(noField.get(obj));
    }
}

通过反射打破封装,访问私有属性。

使用 setAccessible(true)打破封装,但会留下漏洞。

package Reflect;

import java.lang.reflect.Field;

public class ReflectTest07_1 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        Class studentClass = Class.forName("Reflect.Student");
        Object obj = studentClass.newInstance();  //obj是Student的对象(底层调用无参数构造方法)
        //获取no属性(根据属性的名称来获取Field)
        Field noField = studentClass.getDeclaredField("name");

        //打破封装(但使用反射机制打破封装会留下漏洞)
        //这样设置完后,在外部也是可以访问private的
        noField.setAccessible(true);

        //给obj对象(Student对象)的no属性赋值
        noField.set(obj, "张三");

        //读取属性的值
        //两个要素:获取obj对象的no属性的值
        System.out.println(noField.get(obj));
    }
}

 

5.4、使用反射获取类中的方法

UserService类
package Reflect;

public class UserService {
    public boolean login(String username, String password) {
        return true;
    }

    public boolean loginout() {
        return true;
    }
}

获取UserService类中的方法

package Reflect;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ReflectTest08 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取类
        Class userServiceClass = Class.forName("Reflect.UserService");

        //获取所有的 Method(包括私有)
        Method[] methods = userServiceClass.getDeclaredMethods();

        //遍历所有Method
        for (Method method : methods) {
            //获取修饰符
            System.out.print(Modifier.toString(method.getModifiers()) + " ");
            //获取方法的返回值类型
            System.out.print(method.getReturnType().getSimpleName() + " ");

            //获取方法名
            System.out.print(method.getName() + " ");

            //获取方法的形参
            Class[] parameter = method.getParameterTypes();
            for (Class p : parameter) {
                System.out.print(p.getSimpleName() + " ");
            }
            System.out.println();
        }
    }
}

java反射基础知识整理

 

5.5、通过反射机制调用类中的方法

UserService类
package Reflect;

public class UserService {
    public boolean login(String username, String password) {
        if (username.equals("2020") && password.equals("123")) {
            return true;
        }
        return false;
    }

    public boolean loginout() {
        return true;
    }
}

使用反射调用UserService类中的login方法

package Reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/*
通过反射机制调用对象的方法
 */
public class ReflectTest9 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //获取类
        Class userServiceClass = Class.forName("Reflect.UserService");
        //创建对象
        Object obj = userServiceClass.newInstance();
        //获取Method  方法名:login  形参 String,String
        Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class, String.class);
        Object result = loginMethod.invoke(obj, "2020", "123"); //调用方法传入形参获取返回值
        System.out.println(result);
    }
}

通过反射机制,让代码更具有通用性,可变化的内容都是写在配置文件中的,将来修改配置文件后,创建的对象不一样,调用的方法也不同了。但是java代码不需要做任何改动。

 

5.6、通过反射调用构造方法


newInstance()可以调用无参构造方法(若对象的类中没有无参构造方法,会报错)
getDeclaredConstructor()可以获取有参数的构造方法

一个实例

User类

package Reflect;

public class User {
    private int no;
    private String sex;
    private String name;

    public User() {
    }

    public User(int no, String sex, String name) {
        this.no = no;
        this.sex = sex;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "no=" + no +
                ", sex='" + sex + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}
package Reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/*
使用反射机制获取构造方法
 */
public class ReflectTest10 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class c = Class.forName("Reflect.User");  //获取类对象
        Object obj = c.newInstance();  //调用无参构造方法
        System.out.println("调用无参构造方法:" + obj);

        /**
         * 调用有参构造方法
         */
        //先获取到这个有参数的构造方法
        Constructor constructor = c.getDeclaredConstructor(int.class, String.class, String.class);
        //调用构造方法new对象
        Object newobj = constructor.newInstance(2020, "张三", "男");
        System.out.println("调用有参构造方法:" + newobj.toString());
    }
}

java反射基础知识整理

 

5.7、使用反射获取类的父类和父接口

获取String类的父类和实现的接口

package Reflect;

/*
使用反射机制获取父类和父接口
 */
public class ReflectTest11 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class stringClass = Class.forName("java.lang.String");

        //获取String的父类
        Class superClass = stringClass.getSuperclass();
        System.out.println("String的父类为:" + superClass.getName());
        System.out.println();
        //获取String类实现的所有接口
        Class[] interfaces = stringClass.getInterfaces();
        System.out.println("String类实现的接口:");
        for (Class in : interfaces) {
            System.out.println(in.getName());
        }
    }
}

java反射基础知识整理

本文地址:https://blog.csdn.net/weixin_42032770/article/details/107631712

相关标签: java基础知识