java 反射(reflection)class Method Field Constructor

反射

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。
1、静态加载:编译时加载相关的类,如果没有就报错,依赖性强。
2、动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性。

类加载时机

  1. 当创建对象时(new) 静态加载
  2. 当子类被加载时,父类也加载 静态加载
  3. 调用类中的静态成员时 静态加载
  4. 通过反射(Class.forName(“xxx.xxx.xxx”))动态加载

优点

可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支持。

缺点

使用反射基本基于解释执行,对执行速度有影响。

java reflection

1.反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性和方法。反射在设置模式和框架底层都会用到。
2、加载完类之后,在堆中就产生了一个class类型的对象,一个类只有一个Class对象,这个对象包括了类的完整结构信息,通过这个对象得到类的结构。这个Class对象就想一面镜子,透过这个镜子可以看到类的结构,所以形象的成为反射。
java 反射(reflection)class Method Field Constructor_第1张图片

1、第一阶段编译阶段,此阶段内我们在Ide中编译代码,编译后通过javac生成class字节码,进而形成jar包。
2、程序运行阶段,程序运行阶段,使用到某一个对象时,会首先通过类加载器加载Class对象。Class对象中会将.class字节码中的属性和方法都对象化。
3、类加载器加载对象后,会在对中创建对应的对象(Cat对象),Cat对象与class对象具有特定的关联。
4、反射则是通过java特有的方式调用类加载器生成class类,并根据生成的class类对对象进行操作。

反射类

Class

1、class类也是类,继承自Object类
java 反射(reflection)class Method Field Constructor_第2张图片
2、Class类不是new出来的,是系统通过类加载器生成的。
java 反射(reflection)class Method Field Constructor_第3张图片
java 反射(reflection)class Method Field Constructor_第4张图片

3、对于某个类的类对象,在堆中仅存在一份Class类对象,因为类仅会加载一次。

Class cls = Class.forName(props.getProperty("className"));
// 获取声明的类之后,创建类对应的对象
Object o = cls.newInstance();

Class cls2 = Class.forName(props.getProperty("className"));
Log.i(TAG, String.valueOf(cls.hashCode() == cls2.hashCode()));

输出结果:

2023-03-22 15:52:41.438 27368-27368/cn.jj.reflectionkk I/JJWorld.MainActivity: true

4、每个对象都会记录自己是哪个Class类对象生成的。
5、通过Class对象,可以完整的得到一个类的完整结构。
6、class类对象存在于堆中。
7、类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码、变量名、方法名、访问权限等)

class常用方法

public class MainActivity extends AppCompatActivity {

    private static String TAG = "JJWorld.MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Properties props = new Properties();
        try {
            props.load(getAssets().open("re.properties"));
            // 1、通过java的反射创建配置文件中配置的类
            // 表示不确定的Java类型
            Class<?> cls = Class.forName(props.getProperty("className"));
            // 2、输出cls
            Log.i(TAG, "cls:" + cls);
            // 3、输出运行类型
            Log.i(TAG, "cls运行时对象:" + cls.getClass());
            // 4、得到包名
            Log.i(TAG, "包名:" +cls.getPackage().getName());
            // 5、输出全类名
            Log.i(TAG,"全类名:"+ cls.getName());
            // 6、通过cls创建实例对象
            Cat cat = (Cat) cls.newInstance();
            Log.i(TAG,"cat:" + cat);
            // 7、通过反射获取属性
            Field field = cls.getField("name");
            if (BuildConfig.DEBUG) Log.d(TAG, "field.get(cls):" + field.get(cat));
            // 8、通过反射给属性赋值
            field.set(cat,"大黄");
            if (BuildConfig.DEBUG) Log.d(TAG, "field.get(cls):" + field.get(cat));
            // 9、获取所有的属性
            Field[] fields = cls.getFields();
            for (Field field1 : fields) {
                if (BuildConfig.DEBUG) Log.d(TAG, field1.getName() + "--------" + field1.get(cat));
            }

        } catch (Exception e) {
            Log.e(TAG, "Exception " + e.getMessage());
        }
    }
}

输出结果

2023-03-22 17:43:38.733 20737-20737/cn.jj.reflectionkk I/JJWorld.MainActivity: cls:class cn.jj.reflectionkk.Cat
2023-03-22 17:43:38.733 20737-20737/cn.jj.reflectionkk I/JJWorld.MainActivity: cls运行时对象:class java.lang.Class
2023-03-22 17:43:38.733 20737-20737/cn.jj.reflectionkk I/JJWorld.MainActivity: 包名:cn.jj.reflectionkk
2023-03-22 17:43:38.733 20737-20737/cn.jj.reflectionkk I/JJWorld.MainActivity: 全类名:cn.jj.reflectionkk.Cat
2023-03-22 17:43:38.733 20737-20737/cn.jj.reflectionkk I/JJWorld.MainActivity: cat:cn.jj.reflectionkk.Cat@32a447b
2023-03-22 17:43:38.733 20737-20737/cn.jj.reflectionkk D/JJWorld.MainActivity: field.get(cls):大橘
2023-03-22 17:43:38.733 20737-20737/cn.jj.reflectionkk D/JJWorld.MainActivity: field.get(cls):大黄
2023-03-22 17:43:38.733 20737-20737/cn.jj.reflectionkk D/JJWorld.MainActivity: age--------13
2023-03-22 17:43:38.733 20737-20737/cn.jj.reflectionkk D/JJWorld.MainActivity: name--------大黄

Field

Field对象,表示某个类的成员变量。

1、反射只能获取public属性。

测试 修改通过反射获取的属性为对象已有属性且属性为Public

Cat类如下:

public class Cat {
    private static final String TAG = "JJWorld.Cat";

    public String name = "大橘";

    public void hi(){
        Log.d(TAG,"cat say hello");
    }

    public void cry(){
        Log.d(TAG,"cat cry");
    }
}
public class MainActivity extends AppCompatActivity {

    private static String TAG = "JJWorld.MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Properties props = new Properties();
        try {
            props.load(getAssets().open("re.properties"));
            // 通过java的反射创建配置文件中配置的类
            Class cls = Class.forName(props.getProperty("className"));
            // 获取声明的类之后,创建类对应的对象
            Object o = cls.newInstance();

            // 通过反射,可以获取对象的属性,Android运行环境与jdk不同。
            // 对于没有的属性,java获取时会报错,android获取时如果没有则输出 className:属性
            Field nameField = cls.getField("name");
            Log.i(TAG, nameField.get(o).toString());
        }catch (Exception e){
            Log.e(TAG, "className:" + e.getMessage());
        }
    }
}

输出

2023-03-22 11:22:09.111 21288-21288/cn.jj.reflectionkk I/JJWorld.MainActivity: 大橘

Method

Method对象代表某个类的方法。

Constructor

代表类的构造方法,Constructor对象表示构造器

案例

public class MainActivity extends AppCompatActivity {

    private static String TAG = "JJWorld.MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Properties props = new Properties();
        try {
            props.load(getAssets().open("re.properties"));
            // 通过java的反射创建配置文件中配置的类
            Class cls = Class.forName(props.getProperty("className"));
            // 获取声明的类之后,创建类对应的对象
            Object o = cls.newInstance();

            // 无形参的构造器
            Constructor constructor = cls.getConstructor();
            Log.i(TAG,constructor.toString());

            // String.class表示的是String类的class对象
            Constructor constructor1 = cls.getConstructor(String.class);
            Log.i(TAG,constructor1.toString());
            
        }catch (Exception e){
            Log.e(TAG, "className:" + e.getMessage());
        }
    }
}

输出:

2023-03-22 11:43:22.873 21999-21999/cn.jj.reflectionkk I/JJWorld.MainActivity: public cn.jj.reflectionkk.Cat()
2023-03-22 11:43:22.873 21999-21999/cn.jj.reflectionkk I/JJWorld.MainActivity: public cn.jj.reflectionkk.Cat(java.lang.String)

获取反射类的方法

1、已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能会抛出ClassNotFoundException

应用场景:多用于配置文件,读取类全路径,加载类

具体实现如下:

public class MainActivity extends AppCompatActivity {

    private static String TAG = "JJWorld.MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Properties props = new Properties();
        try {
            // 获取class对象的方式

            // 1、已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能会抛出ClassNotFoundException
            // 应用场景:多用于配置文件,读取类全路径,加载类
            props.load(getAssets().open("re.properties"));
            // 通过java的反射创建配置文件中配置的类
            Class<?> cls = Class.forName(props.getProperty("className"));
            Log.i(TAG,"cls:" + cls + " hashcode:" + cls.hashCode());

            // 2、类型.class
            Class cls2 = Cat.class;
            Log.i(TAG,"cls2:" + cls2 +" hashcode:" + cls2.hashCode());

            // 3、通过对象.getClass获取  对象的运行类型就是对应的 Class类对象对应类型
            Cat cat = new Cat();
            Class<?> cls3 = cat.getClass();
            Log.i(TAG,"cls3:" + cls3 + " hashcode:" + cls3.hashCode());

            // 4 通过类加载器来获取类的Class对象
            // 先得到类加载器 car
            ClassLoader classLoader = cat.getClass().getClassLoader();
            // 通过类加载去获得class对象
            Class<?> cls4 = classLoader.loadClass(props.getProperty("className"));
            Log.i(TAG,"cls4:" + cls4 + " hashcode:" + cls4.hashCode());

            // 5、基本数据类型的hashCode
            Class<Integer> type1 = int.class;
            Class<String> stringClass = String.class;
            Log.i(TAG,"type1:" + type1 + " hashcode:" + type1.hashCode());
            Log.i(TAG,"stringClass:" + stringClass + " hashcode:" + stringClass.hashCode());

            // 6、包装数据类型的hashCode
            Class<Integer> type2 = Integer.TYPE;
            Log.i(TAG,"type2:" + type2 + " hashcode:" + type2.hashCode());

        } catch (Exception e) {
            Log.e(TAG, "Exception " + e.getMessage());
        }
    }
}

输出:

2023-03-22 18:11:02.267 23876-23876/cn.jj.reflectionkk I/JJWorld.MainActivity: cls:class cn.jj.reflectionkk.Cat hashcode:53101691
2023-03-22 18:11:02.268 23876-23876/cn.jj.reflectionkk I/JJWorld.MainActivity: cls2:class cn.jj.reflectionkk.Cat hashcode:53101691
2023-03-22 18:11:02.268 23876-23876/cn.jj.reflectionkk I/JJWorld.MainActivity: cls3:class cn.jj.reflectionkk.Cat hashcode:53101691
2023-03-22 18:11:02.268 23876-23876/cn.jj.reflectionkk I/JJWorld.MainActivity: cls4:class cn.jj.reflectionkk.Cat hashcode:53101691
2023-03-22 18:11:02.268 23876-23876/cn.jj.reflectionkk I/JJWorld.MainActivity: type1:int hashcode:110363710
2023-03-22 18:11:02.268 23876-23876/cn.jj.reflectionkk I/JJWorld.MainActivity: stringClass:class java.lang.String hashcode:201249432
2023-03-22 18:11:02.268 23876-23876/cn.jj.reflectionkk I/JJWorld.MainActivity: type2:int hashcode:110363710

反射调用性能优化

对比反射与正常调用耗时

public class MainActivity extends AppCompatActivity {

    private static String TAG = "JJWorld.MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Properties props = new Properties();
        try {
            props.load(getAssets().open("re.properties"));
            // 通过java的反射创建配置文件中配置的类
            Class cls = Class.forName(props.getProperty("className"));
            // 获取声明的类之后,创建类对应的对象
            Object o = cls.newInstance();

            new Thread(new Runnable() {
                @Override
                public void run() {

                    Method hi = null;
                    try {
                        long startTime = System.currentTimeMillis();
                        hi = cls.getMethod("hi");
                        for (int i = 0; i < 900000; i++) {
                            hi.invoke(o);
                        }
                        long endTime = System.currentTimeMillis();
                        Log.i(TAG,"反射耗时:" + (endTime - startTime));

                        Cat cat = new Cat();
                        startTime = System.currentTimeMillis();
                        for (int i = 0; i < 900000; i++) {
                            cat.hi();
                        }
                        endTime = System.currentTimeMillis();
                        Log.i(TAG,"正常调用耗时:" + (endTime - startTime));


                        startTime = System.currentTimeMillis();
                        hi = cls.getMethod("hi");
                        hi.setAccessible(true);
                        for (int i = 0; i < 900000; i++) {
                            hi.invoke(o);
                        }
                        endTime = System.currentTimeMillis();
                        Log.i(TAG,"反射优化耗时:" + (endTime - startTime));

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }).start();



        }catch (Exception e){
            Log.e(TAG, "Exception " + e.getMessage() );
        }
    }
}

耗时结果:

2023-03-22 12:10:14.315 31796-32430/cn.jj.reflectionkk I/JJWorld.MainActivity: 反射耗时:92
2023-03-22 12:10:14.318 31796-32430/cn.jj.reflectionkk I/JJWorld.MainActivity: 正常调用耗时:3
2023-03-22 12:10:14.397 31796-32430/cn.jj.reflectionkk I/JJWorld.MainActivity: 反射优化耗时:79
2023-03-22 12:10:21.420 32286-32537/cn.jj.reflectionkk I/JJWorld.MainActivity: 反射耗时:97
2023-03-22 12:10:21.423 32286-32537/cn.jj.reflectionkk I/JJWorld.MainActivity: 正常调用耗时:3
2023-03-22 12:10:21.497 32286-32537/cn.jj.reflectionkk I/JJWorld.MainActivity: 反射优化耗时:74

耗时优化

  • 1、Method和Field、Constructor对象都有setAccessible()方法
  • 2、setAccessible()作用是启动和禁用访问安全检查的开关
  • 3、参数为true表示 反射的对象在使用时取消访问检查,提高访问的效率,参数为false时表示反射的对象执行访问检查。

类加载过程图

java 反射(reflection)class Method Field Constructor_第5张图片

reflection 案例1

通过配置文件控制程序

通过外部文件配置,在不修改源码的情况下,来控制程序,也符合设计模式的ocp原则(开闭原则)
例如:使用Properties类,可以读取配置文件,程序中根据读取的文件内容更改程序的逻辑。

Cat类

public class Cat {
    private static final String TAG = "JJWorld.Cat";

    public void hi(){
        Log.d(TAG,"cat say hello");
    }

    public void cry(){
        Log.d(TAG,"cat cry");
    }
}

配置文件

className = com.example.myapplication.Cat
classMethod = cry

主测试程序

public class MainActivity extends AppCompatActivity {
    private static String TAG = "JJWorld.MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Properties props = new Properties();
        try {
            props.load(getAssets().open("re.properties"));
            // 通过java的反射创建配置文件中配置的类
            Class cls = Class.forName(props.getProperty("className"));
            // 获取声明的类之后,创建类对应的对象
            Object o = cls.newInstance();
            // 可以打印创建的类的运行类型
            Log.e(TAG, "className:" + o.getClass());
            // 即在反射中,可以把方法视作对象,获取方法对象之后,通过invoke的方式可以使用此方法
            Method method = cls.getMethod(props.getProperty("classMethod"));
            // 反射,方法.invoke对象
            method.invoke(o);
        }catch (Exception e){
            Log.e(TAG, "className:" + e.getMessage());
        }
    }
}

java 反射(reflection)class Method Field Constructor_第6张图片

你可能感兴趣的:(android,java,android,java)