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