Java反射详解

目录

1 Class类的使用

1.1 动态加载类

2 反射

2.1 概念

2.2 作用

2.2.1 构造器

2.2.2 属性

2.2.3 方法

2.2.4 通过更改配置文件进行系统升级

2.2.5 逃避泛型检察


1 Class类的使用

在面向对象的世界里,万事万物皆为对象:Java中类是对象,是java.lang.Class的对象。本质上讲,一个对象对应的一个不变的、唯一的Class对象,称之为该对象的类类型(class type),它代表着一个类的类型信息。Class类能够实现它所代表的这个类的所有功能,包括创建这个类的实例,获得所有的构造函数,方法,字段值等。(基本数据类型、void关键字等都存在类类型

public class ClassTest1 {
    public static void main(String[] args) {
        //User的实例对象如何表示
        User user1 = new User();
        //User也是个实例对象,Class<User>的实例对象,如何表示呢
        //任何一个类都有一个对应的Class实例对象,该Class实例对象有三种表示方法

        //方法一:本质是告诉我们任何一个类都有一个隐含的静态成员变量class
        Class<User> class1 = User.class;
        //方法二:通过该类实例对象的 getClass() 方法获取
        Class<? extends User> class2 = user1.getClass();

        //这条输出结果为“true”,也证明了一个类的Class是唯一的不变的
        System.out.println(class1==class2);
        //方法三:根据全类名获取Class
        Class class3 = null;
        try {
            //此处为User全类名,我的User在当前包下
            class3 = Class.forName("User");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //以下结果依然为true
        System.out.println(class2==class3);

        //还可以通过类类型创建该类的实例  通过class1、class2、class3创建User的实例对象
        try {
            //必须要有无参构造方法
            User user2 = class1.newInstance();
            //打印“Hello !”
            user2.sayHello();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

}
class User{
    void sayHello() {
        System.out.println("Hello !");
    }
}

1.1 动态加载类

  • 用记事本编译运行以下程序
class Office {
    public static void main(String[] args) {
        if("Word".equals(args[0])) {
            Word word=new Word();
            word.start();
        }
        if("Excel".equals(args[0])) {
            Excel excel=new Excel();
            excel.start();
        }
    }
}

如果没有创建WordExcel类,结果就会报二者类和方法不存在。

  • 现在加上Word
class Office {
    public static void main(String[] args) {
        if("Word".equals(args[0])) {
            Word word=new Word();
            word.start();
        }
        if("Excel".equals(args[0])) {
            Excel excel=new Excel();
            excel.start();
        }
    }
}
class Word{
	public void start() {
		System.out.println("Word Start");
	}
}

可以看到Word不报错了

由此可见使用new创建对象属于静态加载类,无论该类是否能用到,在编译时期就要加载所有可能用到的类,如果编译时期加载的类很多不但影响系统启动速度与性能而且一旦一个类报错改程序将无法运行。

  • 那么能否在运行时再加载需要的类呢?当然可以,这就是动态加载类。更改代码
class Office {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class c=Class.forName(args[0]);
        //通过类类型创建该类的对象,要想让Word和Excel都可以用,要进行统一
        //Word w=(Word)c.newInstance();
        OfficeAble oa=(OfficeAble)c.newInstance();
        oa.start();
    }
}

//添加统一接口
interface OfficeAble{
     void start();
}

//Word实现OfficeAble
class Word implements  OfficeAble{
    @Override
    public void start() {
        System.out.println("word...start...");
    }
}

//Excel实现OfficeAble
//故意将该类注释掉
//class Excel implements  OfficeAble{
//    @Override
//    public void start() {
//        System.out.println("excel...start...");
//    }
//}

现在重新编译Office,输入Word参数时程序正常运行;输入Excel时程序报错,找不到Excel类

2 反射

2.1 概念

Reflection(反射)是指在程序运行期间,能够获取任意一个类的所有属性和方法,能够任意操作一个对象的属性和方法。这是Java被称为动态语言的基础。

2.2 作用

  1. 运行时创建任何一个类的对象(该类必须有无参构造器)
  2. 运行时获取任何一个类的所有构造器、方法和属性
  3. 运行时调用任何一个对象的构造器、方法和属性
  4. 生成动态代理

2.2.1 构造器

  1. 创建构造器类
    public class People {
        //-------------------------------构造器--------------------------------------------
        //无参构造器
        public People() {
            System.out.println("无参构造器");
        }
        //构造器
        public People(String name) {
            System.out.println("一个参数构造器 姓名:"+name);
        }
        //构造器
        public People(String name,int age) {
            System.out.println("两个参数构造器 姓名:"+name+"年龄:"+age);
        }
        //受保护构造器
        protected People(boolean pro) {
            System.out.println("受保护构造器 pro:"+pro);
        }
        //私有构造器
        private People(int age) {
            System.out.println("私有构造器 age:"+age);
        }
    }
  2. 测试类
    class ConstructorTest {
        public static void main(String[] args) throws Exception {
            //1.加载Class对象
            Class clazz = People.class;
    
    
            //2.获取所有公有构造方法
            System.out.println("**********************所有公有构造方法*********************************");
            Constructor[] conArray = clazz.getConstructors();
            for(Constructor c : conArray){
                System.out.println(c);
            }
    
    
            System.out.println("*******************所有的构造方法*************************************");
            conArray = clazz.getDeclaredConstructors();
            for(Constructor c : conArray){
                System.out.println(c);
            }
    
            System.out.println("*****************获取公有、无参的构造方法*******************************");
            //需要一个参数的类型,无参构造器所有类型为null 或者不写
            Constructor con = clazz.getConstructor(null);
    
            System.out.println("con = " + con);
            //调用构造方法
            Object obj = con.newInstance();
    
            System.out.println("******************获取私有构造方法,并调用*******************************");
            con = clazz.getDeclaredConstructor(boolean.class);
            System.out.println(con);
            //调用构造方法
            con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
            obj = con.newInstance(false);
        }
    }

2.2.2 属性

  1. 创建属性类
    public class People {
    
        public People() { }
        public String name;
        protected int age;
        char sex;
        private int height;
    
        //添加height的get方法
        public int getHeight() {
            return height;
        }
    }
  2. 创建测试类
    class FieldTest {
        public static void main(String[] args) throws Exception {
            //1.获取Class对象
            Class peopleClass = People.class;
            //2.获取字段
            System.out.println("************获取所有公有的字段********************");
            Field[] fieldArray = peopleClass.getFields();
            for(Field f : fieldArray){
                System.out.println(f);
            }
            System.out.println("****************获取所有的字段***********************");
            fieldArray = peopleClass.getDeclaredFields();
            for(Field f : fieldArray){
                System.out.println(f);
            }
            System.out.println("*************获取公有字段、并调用***********************************");
            Field f = peopleClass.getField("name");
            System.out.println(f);
            //获取一个对象
            Object obj = peopleClass.getConstructor().newInstance();//产生Student对象--》Student stu = new Student();
            //为字段设置值
            f.set(obj, "刘德华");//为Student对象中的name属性赋值--》stu.name = "刘德华"
            //验证
            People people= (People) obj;
            System.out.println("验证姓名:" + people.name);
    
    
            System.out.println("**************获取私有字段、并调用********************************");
            f = peopleClass.getDeclaredField("height");
            System.out.println(f);
            //私有属性必须解除私有限定
            f.setAccessible(true);
            f.set(obj, 176);
            //关闭解除
            f.setAccessible(false);
            System.out.println("身高:" + people.getHeight());
    
        }
    }

2.2.3 方法

  1. 创建方法类
    public class People {
        public void method1(String s){
            System.out.println("方法名为【method1】,公有的,无返回值,参数类型为String:"+s);
        }
        protected void method2(){
            System.out.println("方法名为【method2】,受保护的,无返回值,无参");
        }
        void method3(){
            System.out.println("方法名为【method3】,默认的,无返回值,无参");
        }
        private String method4(int age){
            System.out.println("方法名为【method4】,私有的,有返回值,参数类型为int:"+age);
            return "哈哈";
        }
    }
  2. 创建测试类
    class MethodTest {
        public static void main(String[] args) throws Exception {
            //1.获取Class对象
            Class peopleClass = People.class;
            //2.获取所有公有方法
            System.out.println("***************获取所有的公有方法*******************");
            peopleClass.getMethods();
            Method[] methodArray = peopleClass.getMethods();
            for(Method m : methodArray){
                System.out.println(m);
            }
            System.out.println("***************获取所有的方法***********************");
            methodArray = peopleClass.getDeclaredMethods();
            for(Method m : methodArray){
                System.out.println(m);
            }
            System.out.println("***************获取公有的method1()方法*******************");
            //public Method getMethod(String name,Class<?>... parameterTypes):
            Method m = peopleClass.getMethod("method1", String.class);
            System.out.println(m);
            //实例化一个Student对象
            Object obj = peopleClass.getConstructor().newInstance();
            m.invoke(obj, "刘德华");
    
            System.out.println("***************获取私有的method4()方法******************");
            //public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
            m = peopleClass.getDeclaredMethod("method4", int.class);
            System.out.println(m);
            //解除私有限定
            m.setAccessible(true);
            //需要两个参数,一个是要调用的对象,一个是实参
            Object result = m.invoke(obj, 28);
            System.out.println("返回值:" + result);
        }
    }

2.2.4 通过更改配置文件进行系统升级

  1. 创建类
    public class People {
        public void method1() {
            System.out.println("is method1()");
        }
    }
  2. 创建people.properties配置文件
    class.name=com.joecy.People
    method=method1
  3. 创建测试类
    class Demo {
        public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            String className = getProperties("class.name");
            String methodName = getProperties("method");
            //通过反射获取Class对象
            Class peopleClass = Class.forName(getProperties("class.name"));
            //2获取method1()方法
            Method m = peopleClass.getMethod(getProperties("method"));
            //3.调用method1()方法
            m.invoke(peopleClass.getConstructor().newInstance());
        }
    
        public static String getProperties(String name) throws IOException {
            String value = "";
            Properties pps = new Properties();
            pps.load(new FileInputStream("people.properties"));
            //得到配置文件的名字
            Enumeration enum1 = pps.propertyNames();
            while (enum1.hasMoreElements()) {
                String key = (String) enum1.nextElement();
                if (key.equals(name)) {
                    value = pps.getProperty(key);
                }
            }
            return value;
        }
    }
  4. 如果需要将People升级为SuperPeople

(1)创建SuperPeople类

public class SuperPeople {
    public void method2() {
        System.out.println("is method2()");
    }
}

(2)更改配置文件

class.name=com.joecy.SuperPeople
method=method2

输出:is method2()

2.2.5 逃避泛型检察

原理:类经过编译之后会将泛型擦除

public class Demo1 {
    public static void main(String[] args) throws Exception {
        //向String集合中添加Integer数值
        ArrayList<String> strList = new ArrayList<>();
        strList.add("hello");
        strList.add("world");

        //获取ArrayList的Class对象
        Class listClass = strList.getClass();
        //获取add()方法
        Method method = listClass.getMethod("add", Object.class);
        //调用add()方法
        method.invoke(strList, 1);
        method.invoke(strList, 2);

        //遍历集合
        for (Object o : strList) {
            System.out.println(o);
        }
    }
}
只有把命运掌握在自己手中,从今天起开始努力,即使暂时看不到希望,也要相信自己。因为比你牛几倍的人,依然在努力。
原文地址:https://www.cnblogs.com/freesky168/p/14358235.html