java反射基础知识整理
目录
3、使用Class.forName()方法加载类的静态代码块
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());
}
}
}
本文地址:https://blog.csdn.net/weixin_42032770/article/details/107631712