javase-reflect-210625-01

javase-reflect-210625-01

  • 反射的理解

反射机制有什么用?

通过java语言中的反射机制可以操作字节码文件
类似于黑客。(可以读和修改字节码文件)
通过反射机制可以操作代码片段(class文件)

反射机制的相关类的包

java.lang.reflect.*;

反射机制的相关的重要类

java.lang.Class					代表整个字节码,代表一个类型。   代表整个类
java.lang.reflect.Method		 代表字节码中的方法字节码。		代表类中的方法
Java.lang.reflect.Constructor	  代表字节码中的构造方法字节码。  代表类中的构造方法
java.lang.reflect.Fidld			 代表字节码中的属性字节码。	    代表类中的成员变量(静态变量,实例变量)

// java.lang.Class:
	public class User{
        
        // Field
    	int no;
        
        // Constructor
        public User(){
            
        }
        public User(int no){
            this.no = no;
        }
        
        // Method
        public void setNo(int no){
            this.no = no;
        }
        public int getNo(){
            return no;
        }
    }

反射实例

01.获取Class

ReflectTest01.java

package bgy_reflect01;
/*
    字节码文件载入到JVM中的时候,只有一份
    要操作一个类的字节码,首先需要获取这个类的字节码文件
    有三种方式获取
        1) Class c = Class.forName("完整的类名带包名");
        2) Class c = 引用.getClass();
        3) Class c = 任何类型.class;
 */

import java.util.Date;

public class ReflectTest01 {
    public static void main(String[] args) {

        /*
            ********************
            ******获取方法01*****
            ********************
            Class.forName()
                1. 静态方法
                2. 方法的参数是一个字符串
                3. 字符串需要的是一个完整类名
                4. 完整类名必须带有包名。java.lang包也不能省略
         */
        Class c1 = null;
        Class c2 = null;
        Class c3 = null;
        Class c4 = null;
        try {
            c1 = Class.forName("java.lang.String");     // c1代表String.class文件,或者c1代表String类型
            c2 = Class.forName("java.util.Date");       // c2代表Data类型
            c3 = Class.forName("java.lang.Integer");    // c3代表Integer类型
            c4 = Class.forName("java.lang.System");     // c4代表System类型
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


        /*
            ********************
            ******获取方法02*****
            ********************
            java中任何一个对象都有getClass()方法
        */
        String s = "bgy";
        Class x = s.getClass();         // x代表String.class字节码文件,x代表String类型
        // 字节码文件载入到JVM中的时候,只有一份,
        // 上面运行到c1的时候就存在了字节码文件String.class,所以这里是true
        // 所以c1 和 x 的内存地址指向的同一个String.class文件
        System.out.println(c1 == x);    // true   “ == ”代表判断的是对象的内存地址

        Date date = new Date();
        Class d = date.getClass();
        // d 和 c2 两个变量中保存的内存地址是一样的,都指向方法区中的字节码文件
        System.out.println(d == c2);    // true


        /*
            ********************
            ******获取方法03*****
            ********************
            java中任何一个对象都有,包括基本类型,都有 .class 属性
        */
        Class t1 = String.class;    // t1代表String类型
        Class t2 = int.class;       // t2代表int类型
        Class t3 = Date.class;      // t3代表Date类型
        Class t4 = double.class;    // t4代表double型
        System.out.println(t1 == c1);   // true
        System.out.println(t3 == d);    // true
    }
}
02.获取的Class能干啥

ReflectTest02.java

package bgy_reflect01;

import bgy_reflect01.bean.People;

/*
    获取Class,干啥????
        可以通过反射,创建对象
        通过Class 的 newInstance()方法来实例化对象
        newInstance() 方法内部实际调用了无参构造方法,必须保证无参构造方法存在
 */
public class ReflectTest02 {
    public static void main(String[] args) {

        // 不通过反射机制创建对象
        // 这种方式的代码写死了,只能创建一个People类型的对象
        People p = new People();
        System.out.println(p);      // bgy_reflect01.bean.People@1b6d3586

        // 通过反射机制创建对象
        try {
            Class c = Class.forName("bgy_reflect01.bean.People");       // c代表People类型

            // newInstance() 这个方法会调用People的无参构造方法,完成对对象的创建
            // newInstance() 调用的是无参构造,必须保证无参构造存在!!!!!!
            // 如果没有无参构造方法,会出现“实例化异常”
            Object o = c.newInstance();
            System.out.println(o);        // bgy_reflect01.bean.People@4554617c

            People people = (People)o;
            System.out.println(people);   // bgy_reflect01.bean.People@4554617c

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

03.反射的灵活性

ReflectTest03.java

package bgy_reflect01;

import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

/*
    反射灵活性
    java代码写一遍,在不改变java源码的基础上,可以做到不同对象的实例化
    符合OCP原则:对扩展开放,对修改关闭
 */
public class ReflectTest03 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException {

        // 代码不需要改变,只需要修改配置文件,就可以创建出不同的实例对象
        // 通过IO读取People.properties文件
        FileReader reader = new FileReader("src/bgy_reflect01/bean/People.properties");
        // 创建属性类对象Map
        Properties properties = new Properties();
        // 加载
        properties.load(reader);
        // 关闭流
        reader.close();

        // 通过key获取value
        // Properties对象的key和value都是String类型的
        String className = properties.getProperty("className");
        System.out.println(className);

        // 通过反射机制实例化对象
        Class c = Class.forName(className);
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}
04.Class.forName()干了啥?
package bgy_reflect01;
/*
    Class.forName()发生了什么????
        Class.forName() 方法会导致 类加载

    所以说如果只希望一个类的静态代码块执行,其他代码不执行,
    就可以使用:
        Class.forName("完整类名");
    这个方法执行会导致类加载,类加载时,静态代码块执行
 */

public class ReflectTest04 {
    public static void main(String[] args) {

        try {

            // Class.forName() 方法会导致 类加载
            Class c = Class.forName("bgy_reflect01.MyClass");   // MyClass的静态代码块执行!!!

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

    }
}

class MyClass{

    // 静态代码块在类加载时执行,只执行一次
    static{
        System.out.println("MyClass的静态代码块执行!!!");
    }
}
05.获取文件路径
1) 获取路径

ReflectTest05.java

package bgy_reflect01;

import java.io.FileNotFoundException;
import java.io.FileReader;

/*
    获取文件路径问题
    下面代码获取文件的绝对路径,即使换了操作系统(mac,linux等),依旧可以获取绝对路径
    但是,获取的前提必须是:文件需要在类路径下,才能使用
 */
public class ReflectTest05 {
    public static void main(String[] args) throws FileNotFoundException {
        /*
            这种方式只有在IDEA中有效,可能换一个ide,它的默认根路径就不是project的根了
            IDEA 中默认的当前路径是project的根
            这种方式移植性差
         */
//        FileReader reader = new FileReader("bgy_reflect01/bean/People.properties");


        /*
            以下这种方式,代码即使换路径了,这样编写依旧通用
            
            以下方式前提:这个文件必须在类路径下,
            即src路径下,
            src就是类的根路径
         */
        // Thread.currentThread() 当前线程
        // getContextClassLoader() 线程对象方法,获取当前线程的类加载器对象
        // getResource() 获取资源,类加载器对象的方法,档期线程的类加载器默认从类的根路径下加载资源
        // 拿到一个文件的绝对路径(从类的根路径下作为起点开始)
        String path = Thread.currentThread().getContextClassLoader()
                .getResource("bgy_reflect01/bean/People.properties").getPath();
        System.out.println(path);
    }
}
2) IO+Properties方法获取
package bgy_reflect01;

import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class IOPropertiesTest {
    public static void main(String[] args) throws IOException {

        // 方法01
        // 获取文件的绝对路径
//        String path = Thread.currentThread().getContextClassLoader()
//                .getResource("bgy_reflect01/bean/People.properties").getPath();
//        FileReader reader = new FileReader(path);

        // 方法01
        // 获取文件的绝对路径
        // 直接以流的形式返回
        InputStream reader = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("bgy_reflect01/bean/People.properties");

        // 获取属性类Map集合
        Properties properties = new Properties();
        // 加载流
        properties.load(reader);
        // 关闭流
        reader.close();
        String className = properties.getProperty("className");
        System.out.println(className);
    }
}
3) ResourceBundle获取
package bgy_reflect01;

import java.util.ResourceBundle;

/*
    资源绑定器获取配置资源
    java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容
    使用下面的方式,属性配置文件xxx.properties必须放在类路径下,即src目录下
 */
public class ResourceBundleTest {
    public static void main(String[] args) {

        // 资源绑定器,只能绑定xxx.properties文件,并且这个文件必须在类路径下
        // 文件扩展名必须是properties
        // 写路径名的时候,路径后面不用谢扩展名  bgy_reflect01/bean/People
        ResourceBundle bundle = ResourceBundle.getBundle("bgy_reflect01/bean/People");

        String className = bundle.getString("className");
        System.out.println(className);
    }
}

我的项目目录结构

People.java
package bgy_reflect01.bean;

public class People {
    public People() {
        System.out.println("这是无参构造方法");
    }
}
People.properties
className=bgy_reflect01.bean.People
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值