使用场景 : 在于 `框架 & 工具类` 底层代码的实现.
作用 : 在程序运行阶段, 动态创建对象. 完成程序代码的通用性.
创建对象:
1. new 类名();
缺点 : 编译阶段确定了创建的对象. 如果不修改源代码, 就无法实现更换对象.
优点 : 代码简单, 阅读性强.
2.反射创建对象;
反射创建对象的优点 : 实现了对象的灵活性. 根据配置文件的信息, 动态创建对应的字符串表示的对象.
- 获取指定 `字符串` 的Class对象. 字符串表示的是一个'全限定类名'.
- 使用获取的Class 对象调用 newInstance() 方法创建一个字符串表示的实例对象.
public static void main(String[] args) throws Exception {
//1.创建properties对象
Properties prop = new Properties();
//2.将配置文件加载到properties对象 配置文件key=value;
prop.load(new FileReader("obj.properties"));
//3.使用getProperty(key)方法获取value,即通过key拿到value即'包名+类名'也就是全限定类名
String className = prop.getProperty("obj");
System.out.println(className);
//4.通过className也就是权限类名 使用class.forName()方法拿到class对象;
Class<?> cls = Class.forName(className);
//5.使用class对象调用newInstance(),创建一个字符串表示的实例对象
Object obj = cls.newInstance();
System.out.println(obj);
}
静态代码块加载配置文件:
特点:
1.时间早,在类被加载的时候执行
2.静态代码块仅被执行一次
public class ReflectionTest2 {
// 定义一个Properties对象
private static Properties prop = null;
// 定义一个静态代码块儿
static {
// 初始化Properties对象
prop = new Properties();
try {
// 加载配置文件到Peoperties对象中
prop.load(new FileReader("obj.properties"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
method1();
method2();
}
public static void method1() throws Exception {
String className = prop.getProperty("obj1");
Class<?> cls = Class.forName(className);
Object obj = cls.newInstance();
System.out.println(obj);
}
public static void method2() throws Exception {
String className = prop.getProperty("obj2");
Class<?> cls2 = Class.forName(className);
Object obj = cls2.newInstance();
System.out.println(obj);
}
}
获取Class对象的三种方式:
-
通过 Class.forName(全限定类名);使用场景 : 配置文件
-
通过 类名.class 属性;使用场景 : 调用方法时的形参类型确定.
-
通过 对象.getClass() 方法.使用场景 : 方法内部确定传入参数对象的具体类型.
public class TestClass {
public static void main(String[] args) throws Exception {
/*
* ClassNotFoundException 类找不到异常
* InstantiationException 实例化异常 (创建对象异常)
* IllegalAccessException 非法访问异常
*/
// 1. Class.forName(全限定类名);
Class<?> cls = Class.forName("reflection.Student");
Object obj = cls.newInstance();
System.out.println(obj);
// 2. 类名.class 属性
// 确定一个唯一的方法 `方法名 + 参数列表`
// introduce(String, int, char)
// introduce(String.class, int.class, char.class) 所用类型都拥有 class 属性.
// int.class, float.class, char.class, boolean.class
Class<?> cls2 = Student.class;
System.out.println(cls2);
Object obj2 = cls2.newInstance();
System.out.println(obj2);
System.out.println(cls == cls2);//ture
// 3. 对象名.getClass() 方法
Student stu = new Student();
show(stu);
}
public static void show(Object obj) throws Exception {
Class<?> cls = obj.getClass();
System.out.println(cls);
Object o = cls.newInstance();
System.out.println(o);
}
}
反射构造方法:
public class TestConstructor {
public static void main(String[] args) throws Exception {
// 公开无参(public)
Class<?> cls = Class.forName("reflection.Student");
Object obj = cls.newInstance();
System.out.println(obj);
// 私有有参(private)
// 1. 获取私有构造方法对象
// Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
// 参数列表 : Class 类型的可变参数, 可变参数底层就是数组.
Class<?>[] parameterTypes = new Class<?>[]{String.class, int.class};
Constructor<?> constructor = cls.getDeclaredConstructor(parameterTypes);
// 2. 设置私有构造方法的暴力访问
constructor.setAccessible(true);
// 3. 创建对象
// newInstance(Object ... initargs) 需要的是一个 Object 类型的可变参数
Object[] initargs = new Object[]{"Lucy", 18};
Object obj2 = constructor.newInstance(initargs);
System.out.println(obj2);
}
}
反射调用方法:
public class TestMethod {
// args -> arguments 实参 parameter 形参
public static void main(String[] args) throws Exception {
/*
// public
stu.setName("Lucy");
stu.setAge(18);
// private
stu.show(10086);
*/
// 1. 反射创建一个对象
// Object obj = Class.forName("reflection.Student").newInstance();
Class<?> cls = Class.forName("reflection.Student");
Object obj = cls.newInstance();
// 2. 获取方法对象 (public) setName:方法名
Class<?>[] parameterTypes = new Class<?>[]{String.class};
Method method1 = cls.getMethod("setName", parameterTypes);
Object[] arguments = new Object[]{"Lucy"};
method1.invoke(obj, arguments);
System.out.println(obj);
// 3. 调用私有方法 show:方法名
Method method2 = cls.getDeclaredMethod("show", int.class);
// 4. 暴力访问
method2.setAccessible(true);
// 5. 调用方法
method2.invoke(obj, 10086);
}
}
反射设置并获取属性:
public class TestField {
public static void main(String[] args) throws Exception {
// 1. 反射创建一个对象
Class<?> cls = Class.forName("reflection.Student");
Object obj = cls.newInstance();
// 2. 获取属性对象 desc:公开属性
Field field1 = cls.getField("desc");
// 3. 设置属性
field1.set(obj, "学霸");
System.out.println(obj);
// 获取私有属性对象 name:私有属性
Field field2 = cls.getDeclaredField("name");
// 暴力访问
field2.setAccessible(true);
// 设置属性
field2.set(obj, "Lucy");
System.out.println(obj);
}
}
类加载器:
Class 类的对象是 `类加载器` 为我们创建的.
Class 对象在程序中是无法直接创建的.
Class 类的构造方法是私有的. 该构造方法接收一个参数. 类型为 `ClassLoader` 类加载器. 只有Java虚拟机可以创建Class对象.
Java 虚拟机类加载器的类型 :
- 引导类加载器: 加载运行 `JRE` 中所有以 lib 结尾的包.
- 扩展类加载器: 加载运行时 `JRE` 中所有以 ext 结尾的包. (extension) Ext 包中的类是给 JRE 运行环境提供辅助和扩展
- 应用类加载器: 加载程序员编写的 `类`. 第三方框架和工具类.
三者之间的关系 : 应用类加载器 继承 扩展类加载器 继承 引导类加载器