62-反射

61 阅读7分钟
 /*
    Javabean类的介绍
        1.所有成员变量必须private修饰
        2.必须提供对应的get和set方法
        3.必须提供空参构造(满参构造是可选的) 
 */ 
反射--通过获取Class类型的对象从而文件中的成员变量/成员方法/构造方法并执行
类名.class通过类名的属性class获取,多用于参数的传递
对象.getClass(),通过对象的getClass()方法获取,多用于获取字节码的方式
Class.forName("全类名")获取字符串路径,用于配置文件,将类名定义在配置文件中
### 类的加载器
(1)BootstrapClassLoader 根类加载器,无法获取   比如System,String等。 
 (2)ExtClassLoader 扩展类加载器-jar包的加载 
  (3)AppClassLoader 系统类加载器-java写的
三者之间不存在继承关系,最终的父类 java.lang.ClassLoader  
### 类的加载时机
//父类
public class Person {
    public static int NUM = 100;
    static {
        System.out.println("Person 静态代码块执行了...");
    }
    public static void method() {
        System.out.println(NUM);
    }
}
//子类
public class Student extends Person {
    static {
        System.out.println("Student...代码块执行了....");
    }
} 
*/
//测试类
public class Demo02LoadClass {
    public static void main(String[] args) throws ClassNotFoundException {
         类(类的.class文件)的加载时机
        1. 创建类的实例。
        //Person p = new Person();
        2. 类的静态变量,或者为静态变量赋值。
        //System.out.println(Person.NUM);
        3. 类的静态方法。
        //Person.method(); 
        //4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
        //Class<?> clazz = Class.forName("itheima02.Person"); 
        //5. 初始化某个类的子类。
        Student stu = new Student();
        6. 直接使用java.exe命令来运行某个主类。 
    }
}
1获取类加载器   ClassLoader=String/DNSNameService/Demo03ClassLoader.class.getClassLoader(); 
2getParent(): 返回委托的父类加载器。  ClassLoader p = c.getParent();
3获取Class类型 getSimpleName(): 获得简单类名,只是类名,没有包
4获取Class类型 @1getName(): 获取完整类名,包含包名+类名
               返回与带有给定字符串名的类或接口相关联的 Class 对象。
              @2建议使用forName(String className)
             参数: String className 
User user = new User();    
Class c3 = Class.forName("domain.User");
5newInstance()创建class新实例要求:类必须有public的无参数构造方法
 newInstance() 空参构造方法创建对象。
 newInstance(Object... initargs)  根据指定参数创建对象。
6Constructor是构造方法类,用来实例化对象。 
     Constructor getConstructor(Class... parameterTypes)            
    根据参数类型获取构造方法对象,只能获得public修饰的构造方法。
    如果不存在对应的构造方法,则会抛出 java.lang.NoSuchMethodException 异常
     参数是可变参数,调用此方法时,可以不写参数,获取的空参构造
     可以写参数,给定的参数必须是Class对象
     比如: 参数 String name,int age
     调用此方法: String.class,int.class  
7.指定参数私有构造方法对象调用setAccessible方法,取消 Java 语言访问检查 con.setAccessible(true);      
  false,实施 Java 语言访问检查private 有效 true 取消 Java 语言访问检查 private 失效
8 Method类通过Method对象可以调用方法。
getMethods()数组  getMethod getDeclaredMethod可以获取private的
9invoke ---Object invoke(Object obj, Object... args) 
  	根据参数args调用对象obj的该成员方法 如果obj=null,则表示该方法是静态方法  
         Object value = setNameMethod.invoke(user, "张三"); 
        //执行get方法,获取成员变量的值
        String name = (String) getNameMethod.invoke(user); 
         //5.private修饰的方法对应的Method对象调用调用invoke方法,执行对应的功能
        char[] chs = (char[]) m.invoke(user,"helloworld");
        System.out.println(new String(chs));
10 ClassLoader的子类  InputStream getResourceAsStream(String name): 返回读取指定资源的输入流。   
 如果配置文件写在src根下:  String name: 只需要写文件名.扩展名 config.properties
 如果配置文件写在src/itheima02下: String name: itheima02/config.properties
11 ResourceBundle类:获取资源的类
   注意: 该类是抽象类,不能直接创建对象,可以创建子类对象
   getBundle(String baseName): 用于获取该抽象类的子类对象
   参数:  String baseName: 如果配置文件写在src根下,此处只需要写文件名(不需要写扩展名) 
   getString(String key): 根据String类型的属性名,获取String类型属性值(根据键找值)
   
public class Demo04GetConstructor {
    public static void main(String[] args) throws Exception {
        //1.获取Class类型的对象(推荐使用: Class.forName)
        Class<?> clazz = Class.forName("domain.User");
        //获取所有的public修饰的所有构造方法getConstructors
        Constructor<?>[] cons = clazz.getConstructors();
        //增强for遍历
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }
        System.out.println("----------------");
        //获取public修饰的空参构造方法对象
        Constructor<?> con1 = clazz.getConstructor();
        System.out.println(con1);
        //获取public修饰的第一个参数是String类型,第二个参数是int类型的构造方法对象
        getConstructor(Class... parameterTypes)调用此方法时,可以不写参数,获取的空参构造可以写参数,给定的参数必须是Class对象  比如: 参数 String name,int age 调用此方法: String.class,int.class 
        Constructor<?> con2 = clazz.getConstructor(String.class, int.class);
        System.out.println(con2);
        //获取public修饰的参数是int类型的构造方法对象
        //报异常: NoSuchMethodException,没有这个方法异常,因为方法是private修饰
        //Constructor<?> con3 = clazz.getConstructor(int.class);
        //System.out.println(con3);
        //获取private修饰的参数是int类型的构造方法对象
        Constructor<?> con4 = clazz.getDeclaredConstructor(int.class);
        System.out.println(con4);
        getDeclaredConstructors  获取private所有的构造方法
    }
}
获取空参构造方法并运行
                    获取到了空参构造方法对象
                Constructor con = ...; 
                 Object obj = con.newInstance();
                获取到了满参构造方法对象   Constructor con = ...; 
                Object obj = con.newInstance("zhangsan",18);
public class Demo05NewInstance {
    public static void main(String[] args) throws Exception {
        //1.获取Class类型的对象(推荐使用: Class.forName)
        Class clazz = Class.forName("domain.User");
 反射带参构造方法并运行        
        //2.Class对象调用getConstructor方法,获取空参构造方法对象
        Constructor con = clazz.getConstructor();
        //3.构造方法对象调用newInstance方法,创建一个具体的对象
        User user = (User) con.newInstance("张三",18);
        System.out.println(user);
    }
}
     获取public修饰符的所有成员方法
     获取public修饰的名称为toString的没有参数的方法
     获取public修饰的名称为setName的参数为String类型的方法
     获取public修饰的名称为getSum的参数为两个int类型的方法
     获取public修饰的名称为my2CharArray的参数为String类型的方法   
public class Demo01GetMethod {
    public static void main(String[] args) throws Exception {
        //1.获取Class类型的对象(推荐使用: Class.forName)
        Class<?> clazz = Class.forName("domain.User");
        //2.获取所有public修饰包含父类的成员方法对象组成的数组
        Method[] ms = clazz.getMethods();
        //增强for遍历
        for (Method m : ms) {
            System.out.println(m);
        }
        System.out.println("----------------");
        //获取public修饰的名称为toString的没有参数的方法
        Method m1 = clazz.getMethod("toString");
        System.out.println(m1);
        //获取public修饰的名称为setName的参数为String类型的方法
        Method m2 = clazz.getMethod("setName", String.class);
        System.out.println(m2);
        //获取public修饰的名称为getSum的参数为两个int类型的方法
        Method m3 = clazz.getMethod("getSum", int.class, int.class);
        System.out.println(m3);
        //获取public修饰的名称为my2CharArray的参数为String类型的方法
        //该方法private修饰,无法获取报出异常
        //Method m4 = clazz.getMethod("my2CharArray", String.class);
        //System.out.println(m4);
        //获取private修饰的名称为my2CharArray的参数为String类型的方法
        Method m5 = clazz.getDeclaredMethod("my2CharArray", String.class);
        System.out.println(m5);
    }
}
反射案例 写一个"框架"不改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
步骤:   1.定义配置文件config.properties,以属性名=属性值
                className=itheima02.Dog
                methodName=eat
   注意:   配置文件config.properties必须写在src根路径下
   src下写的java源代码,项目完成后,给客户的是.class文件
   配置文件会跟着.class文件,到类路径(存储.class文件的路径)下,同步的              
//抽象父类
public abstract class Animal {
    public abstract void eat();
}
//子类
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼...");
    }
}
//子类
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头....");
    }
}
public class Demo03ReflectTest {
    之前    1 props.load(new FileInputStream("day14\\src\\config.properties")); 
         //2.需要把配置文件信息,加载到Properties集合对象中
        Properties props = new Properties();
        //获取类加载器 
        ClassLoader classLoader = Demo03ReflectTest.class.getClassLoader(); 
        //通过类加载器,获取资源的字节输入流
        InputStream is = classLoader.getResourceAsStream("config.properties");  
        props.load(is); 
    
        //2.获取配置文件中定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName"); 
    }
}

public class Demo04ReflectTest {
    public static void main(String[] args) throws Exception {
        //创建子类管理类ResourceBundle的对象
        ResourceBundle resourceBundle = ResourceBundle.getBundle("config");

        //3.获取类的全名称和方法名称
        String className = resourceBundle.getString("className");
        String methodName = resourceBundle.getString("methodName");

        //4.获取类的Class类型的对象
        Class<?> clazz = Class.forName(className);

        //5.根据Class类型的对象获取要执行的方法对应的Method对象
        Method eatMethod = clazz.getMethod("eat");

        //6.Class类型的对象创建一个具体的对象出来
        Object obj = clazz.newInstance();

        //7.Method对象调用invoke方法,执行具体的功能
        eatMethod.invoke(obj);
    }
}