有不懂的可以留言我,虽然我也很菜
Class.forName()读取配置文件举例
以榨汁机榨汁为案例
配置文件config.properties:
reflect_examples.Orange
//注意将配置文件放在src下
//然后写某个类的相对路径就可以了
/**
* 当我们想喝苹果汁就要注释掉喝橘子汁的代码
* 想喝哪种果汁就要注释掉其它果汁的代码,不断修改源代码,不好.
*
* 当我们通过反射的时候,我们可以只修改配置文件,而不需要动源码
*/
public class Example_1_ForName {
public static void main(String[] args) throws ClassNotFoundException, IOException, IllegalAccessException, InstantiationException {
//1. 没有反射,只用到了多态的时候
/*//这就相当于买了一个榨汁机
Juicer juicer = new Juicer();
//这就相当于把苹果放进榨汁机,用榨汁机榨了一杯苹果汁
//juicer.getJuice(new Apple());
//这就相当于把橙子放进榨汁机,用榨汁机榨了一杯橙子汁
juicer.getJuice(new Orange());*/
//2. 用反射和配置文件
//先拿到apple类的字节码文件
//这样写就写死了,配置文件没有起到作用
//Class clazz = Class.forName("main.reflect_examples.Apple");
/**
* 正确写法应该这样子:
* 1. 创建一个流文件,读取一行
* 2. 根据流文件中取到的那一行去创建字节码文件对象
* 3. 通过字节码文件对象创建该字节码文件对象所表示的类的实例,这里相当于创建一个苹果的实例对象
* 4. 我们以后想喝其它水果的果汁,直接改配置文件就可以了
*/
BufferedReader reader = new BufferedReader(new FileReader("src/config.properties"));
Class clazz = Class.forName(reader.readLine());
Fruit fruit = (Fruit) clazz.newInstance();
//榨汁机榨汁
Juicer juicer = new Juicer();
juicer.getJuice(fruit);
}
}
//利用多态把苹果向上抽取为水果
interface Fruit{
public abstract void squeeze();
}
class Apple implements Fruit{
//榨汁功能
@Override
public void squeeze() {
System.out.println("榨了一杯苹果汁!");
}
}
class Orange implements Fruit{
//榨汁功能
@Override
public void squeeze() {
System.out.println("榨了一杯橙子汁!");
}
}
//通过榨汁机榨汁
class Juicer {
/*//把苹果放到榨汁机里通过榨汁机榨苹果汁
public void getJuice(Apple apple) {
apple.squeeze();
}
//把苹果放到榨汁机里通过榨汁机榨苹果汁
public void getJuice(Orange orange) {
orange.squeeze();
}*/
//利用多态后的写法
public void getJuice(Fruit fruit) {
fruit.squeeze();
}
}
通过反射获取带参构造函数并使用
我们上面榨汁机例子中使用到了
newInstance()
这个方法来获取字节码文件所表示的类的实例对象,此时使用的是无参构造函数,当我们想通过带参构造函数创建时,就不能这样子用了,就要用到其他方法.使用案例及步骤:
/**
* 根据带参构造创建实例举例:
* 1. 可以先通过调用Class类的getConstructor(String.class, int.class)获取一个指定的构造函数
* 2. 然后再调用Constructor类的newInstance("张三", 18)方法来创建实例对象
*/
public class Example_1_ForName2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//注意这里的路径是相对路径,idea右键会直接有赋值相对路径的选项
Class clazz = Class.forName("reflect_examples.Person");
//获取该字节码文件对象中所有公共构造方法
Constructor[] constructors = clazz.getConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}
//获取对应构造函数的Constructor对象
Constructor constructor = clazz.getConstructor(String.class, int.class, double.class);
Person person = (Person) constructor.newInstance("李四", 18, 99);
System.out.println(person);
}
}
//创建一个person类
class Person {
private String name;
private int age;
private double score;
public Person(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
通过反射获取成员变量并使用
使用案例及步骤:
/**
* 获取成员变量并使用举例:
* 1. 可以先通过调用Class类的getConstructor(String.class, int.class)获取一个指定的构造函数
* 2. 然后再调用Constructor类的newInstance("张三", 18)方法来创建实例对象
* 3. 通过getFields()获取Class对象所表示的类的所有公共字段
* 千万注意这里是公共字段哈,只能是public的,private,默认,propected的它是访问不到的
* 如果我们字段是private修饰的,我们可以用getDeclaredFields(),它不管什么修饰符修饰都可以访问到
* 4. 通过getField(string name)返回一个Field对象,表示name这个公共字段的对象.
* 也要注意这个修改的属性也得是public的哈
* 如果不是public修饰的,我们就要去除它的权限
* 在反射面前,一切都是赤果果的哈哈哈哈
*/
public class Example_1_ForName3 {
public static void main(String[] args) throws Exception{
//注意这里的路径是相对路径,idea右键会直接有赋值相对路径的选项
Class clazz = Class.forName("reflect_examples.Person");
//获取该字节码文件对象中所有公共构造方法
Constructor[] constructors = clazz.getConstructors();
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}
//获取对应构造函数的Constructor对象
Constructor constructor = clazz.getConstructor(String.class, int.class, double.class);
Person person = (Person) constructor.newInstance("zhannsan", 18, 99);
System.out.println(person);
System.out.println("------------");
//获取所有属性字段
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i]);
}
//获取name属性表示的field对象
Field field = clazz.getDeclaredField("name");
//去除私有权限
field.setAccessible(true);
//修改person对象的属性name的值
field.set(person,"李四");
System.out.println(person);
}
}
//创建一个person类
class Person {
private String name;
int age;
protected double score;
public Person(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
}
通过反射获取方法并使用
注意
getDeclaredMethods()获取方法的时候不能获取继承的方法和实现的接口的方法.
getMethod()可以获取本类及其继承自父类的所有公共方法
使用案例及步骤:
/**
* 获取方法并使用举例:
* 1. 可以通过getDeclaredMethods()获取所有方法
* 2. 通过Class.getMethod(String.class...)
* Class.getDeclaredMethod(Srting.class...)方法可以获取类中的指定方法.
* 3. 调用method.invoke(Object, Object...)可以调用该方法,表示对象obj调用参数为object的方法method
*/
public class Example_1_ForName4 {
public static void main(String[] args) throws Exception{
//获取Class对象
Class clazz = Class.forName("reflect_examples.Person");
//通过无参构造方法创建实例对象
Person person = (Person) clazz.newInstance();
System.out.println("------------");
//查看所有方法
Method[] methods = clazz.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}
//生成eat方法对象
Method eat = clazz.getDeclaredMethod("eat");
//调用方法,即person对象调用方法无参方法eat
eat.invoke(person);
}
}
//创建一个person类
class Person {
public Person() {
}
public void eat() {
System.out.println("今天吃了大龙虾!");
}
}
通过反射越过泛型检查
要求: ArrayList的一个对象,想在这个集合中添加一个字符串数据,怎么整?
- 这里我们就要知道泛型起作用是在编译期,在运行期泛型会被擦除掉,也就是说,我们生成.class文件后,泛型就没了.
使用案例及步骤:
/**
* 要求: ArrayList<Integer>的一个对象想在这个集合中添加一个字符串数据
*/
public class Example_1_ForName5 {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
//获取字节码文件对象
Class clazz = Class.forName("java.util.ArrayList");
//拿到add方法对象
Method method = clazz.getDeclaredMethod("add", Object.class);
//执行add方法,表示list调用add方法,且add方法参数为"樊磊"
method.invoke(list,"樊磊");
System.out.println(list);
}
}
通过反射写一个通用的设置某个对象的某个属性为指定的值
要求: public void setProperty(Object obj, String propertyName, Object value){},此方法可将obj对象中名为propertyName的属性值设置为value
使用案例及步骤:
public class Example_1_ForName6 {
public static void main(String[] args) throws Exception {
User user = new User();
System.out.println(user);
Example_1_ForName6 e = new Example_1_ForName6();
e.setProperty(user, "name", "fan");
System.out.println(user);
}
public void setProperty(Object obj, String propertyName, Object value) throws Exception {
//获取字节码文件对象
Class clazz = obj.getClass();
//获取对应的字段对象
Field field = clazz.getDeclaredField(propertyName);
//擦除其权限
field.setAccessible(true);
//修改值
field.set(obj, value);
}
}
class User {
private String name;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
一个练习
/**
* 要求:
* 1. 给定一个类Student,要求自己新建一个property的配置文件写入类的完整名称
* (就是包名.类名,idea选中类然后复制引用就是的)
* 2. 然后写一个程序,读取这个properties配置文件,获取类的完整名称并加载这个类,用反射的方式运行run方法
*/
public class Example_1_ForName7 {
public static void main(String[] args) throws Exception {
//读取配置文件
BufferedReader br = new BufferedReader(new FileReader("src/Student.properties"));
String s = br.readLine();
//获取类的字节码文件对象
Class clazz = Class.forName(s);
//获取方法对象
Method run = clazz.getDeclaredMethod("run");
//获取该类的构造方法对象
Constructor constructor = clazz.getConstructor(String.class);
//通过构造方法创建对象
Student student = (Student) constructor.newInstance("pipi");
//调用方法
run.invoke(student);
}
}
class Student{
private String name;
public Student(String name) {
this.name = name;
}
public void run() {
System.out.println("学生" + name + "在晨跑!");
}
}
main方法还可以这么写,就是不需要生成method对象,直接调用
public static void main(String[] args) throws Exception {
//读取配置文件
BufferedReader br = new BufferedReader(new FileReader("src/Student.properties"));
String s = br.readLine();
//获取类的字节码文件对象
Class clazz = Class.forName(s);
//获取该类的构造方法对象
Constructor constructor = clazz.getConstructor(String.class);
//通过构造方法创建对象
Student student = (Student) constructor.newInstance("pipi");
//调用方法
student.run();
}