Java核心知识反射

Posted 烟锁迷城

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java核心知识反射相关的知识,希望对你有一定的参考价值。

1、定义

在程序运行中动态获取类的相关属性,这种动态获取类的内容以及动态调用对象的方法和获取属性的机制,就叫做JAVA的反射机制。

  • 对于给定的一个类(class)对象,可以获得这个类(class)对象的所有属性和方法
  • 对于给定的一个对象(new X <? extends Object>),都能够调用它的任意一个属性和方法。

2、优缺点

优点

  1. 增加程序的灵活性,避免固有逻辑写死到程序中
  2. 代码相对简洁,可以提高程序的可用性

代码示例:

public interface Ball {
    void play();
}

public class FootBall implements Ball {
    @Override
    public void play() {
        System.out.println("踢足球");
    }
}

public class BasketBall implements Ball {
    @Override
    public void play() {
        System.out.println("打篮球");
    }
}

public class BallMain {

    public static Ball getInstanceByKey(String key) {
        String packageName = "com.thread.demo.bool.";
        try {
            return (Ball) Class.forName(packageName+key).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class Demo {

    public static void main(String[] args) {
        BallMain.getInstanceByKey("BasketBall").play();
    }
}

缺点

  1. 相比于直接调用,反射有更大的性能开销
  2. 暴露类的内部结构,有安全隐患

调用native方法和在newInstance时,会进行安全检查,这些让性能下降。

3、具体操作

3.1、基本操作

3.1.1、获取类对象的四种方式

Class<User> aClass1 = User.class;
Class<?> aClass2 = Class.forName("com.lc.app.demo.User");
Class<? extends User> aClass3 = new User().getClass();
Class<?> aClass4 = User.class.getClassLoader().loadClass("com.lc.app.demo.User");

3.1.2、获取类的相关类型

//获取类的修饰符
aClass.getModifiers();
//获取类的包名
aClass.getPackage();
//获取全类名
aClass.getName();
//获取父类
aClass.getSuperclass();
//获取类加载器
aClass.getClassLoader();
//获取简单类名
aClass.getSimpleName();
//获取类型实现的所有接口
aClass.getInterfaces();
//获取注解信息
aClass.getAnnotations();

3.1.3、字段的操作

public static void main(String[] args) throws Exception {
    Class<User> userClass = User.class;
    //获取User对象
    User user = userClass.newInstance();
    //获取当前类及其父类的的公有字段
    Field[] fields = userClass.getFields();
    //获取当前类的所有字段
    Field[] declaredFields = userClass.getDeclaredFields();
    //获取指定属性
    Field userNameField = userClass.getDeclaredField("userName");
    //放开操作私有属性的权限
    userNameField.setAccessible(true);
    //赋予属性数值
    userNameField.set(user,"喵喵");
    //获取指定静态属性
    Field addressField = userClass.getDeclaredField("address");
    //赋予静态变量数值,无需指定具体类
    addressField.set(null,"地球村");
}
public class User {

    private String userName;
    public Integer age;
    public static String address;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

3.1.4、方法的操作

public static void main(String[] args) throws Exception {
    Class<User> userClass = User.class;
    //获取User对象
    User user = userClass.newInstance();
    //获取当前类以及其父类的公有方法
    userClass.getMethods();
    //获取当前类的全部方法
    userClass.getDeclaredMethods();
    //获取指定方法
    Method sayHello = userClass.getDeclaredMethod("sayHello");
    //放开私有方法调用
    sayHello.setAccessible(true);
    //调用方法
    sayHello.invoke(user);
    //获取指定静态方法
    Method say = userClass.getDeclaredMethod("say", String.class);
    //调用方法
    say.invoke(null,"hi 喵");
}
public class User {

    public static void say(String msg) {
        System.out.println(msg);
    }

    private static void sayHello() {
        System.out.println("hello");
    }
}

3.1.5、构造器的操作

public static void main(String[] args) throws Exception {

    Class<User> userClass = User.class;
    //获取当前类及其父类的公有方法
    Constructor<?>[] constructors = userClass.getConstructors();
    //获取当前类的所有方法
    Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
    //调用无参构造器
    User user = userClass.newInstance();
    //获取私有构造器
    Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class, Integer.class);
    //允许私有构造器调用
    declaredConstructor.setAccessible(true);
    //调用私有构造器
    declaredConstructor.newInstance("喵喵", 18);

}
public class User {

    private String userName;
    private Integer age;

    public User() {
    }

    private User(String userName, Integer age) {
        this.userName = userName;
        this.age = age;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

3.2、反射对单例的破坏

静态内部类单例代码:

public class InnerSingleton {

    private InnerSingleton(){}

    public static final InnerSingleton getInstance(){
        return InnerHolder.SINGLETON;
    }

    private static class InnerHolder{
        private static final InnerSingleton SINGLETON = new InnerSingleton();
    }
}

反射调用与正常调用:

public static void main(String[] args) throws Exception {

    InnerSingleton instance1 = InnerSingleton.getInstance();
    InnerSingleton instance2 = InnerSingleton.getInstance();
    System.out.println(instance1);
    System.out.println(instance2);
    Constructor<InnerSingleton> declaredConstructor = InnerSingleton.class.getDeclaredConstructor();
    declaredConstructor.setAccessible(true);
    System.out.println(declaredConstructor.newInstance());
}

结果可见反射将单例破坏。
在这里插入图片描述
如果不希望反射被单例破坏,需要禁止在已经存在一个实例对象的情况下调用单例方法

public class InnerSingleton {

    private InnerSingleton(){
		if(InnerHolder.SINGLETON != null){
            throw new RuntimeException("error");
        }
	}

    public static final InnerSingleton getInstance(){
        return InnerHolder.SINGLETON;
    }

    private static class InnerHolder{
        private static final InnerSingleton SINGLETON = new InnerSingleton();
    }
}

以上是关于Java核心知识反射的主要内容,如果未能解决你的问题,请参考以下文章

Java安全-入门篇开发基础(MavenJavaMVC反射类加载代码远程调试)

java 反射代码片段

Java核心知识反射

关于JAVA 反射 基础知识/编码经验的一些总结

关于JAVA 反射 基础知识/编码经验的一些总结

Java核心技术梳理-类加载机制与反射