java反射基础知识整理

目录

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);
    }
}

 

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);  // "=="比较的是地址
    }
}

方式二、通过反射机制获取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

 

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

配置文件:classinfo.properties

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);
    }
}

注:许多高级框架(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类静态代码块执行了");
    }
}

 

延伸:在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);
    }
}

 

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);
    }
}

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();
        }
    }
}

 

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());
    }
}

 

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());
        }
    }
}

おすすめ

転載: blog.csdn.net/weixin_42032770/article/details/107631712