1.概述
以下是SUN提供的反射机制中的类:
类 | 简介 |
---|---|
java.lang.Class | Class c =ox123;对应反射类的整体 |
java.lang.reflect.Constructor | Constructor c =ox456;对应反射类中的构造方法 |
java.lang.reflect.Field | Field f = ox789;对应反射类中的参数对象等 |
java.lang.reflect.Method | Method m = ox963;对应反射类中的方法 |
java.lang.reflect.Modifier | Modifier m = ox741;对应反射类中的各种修饰,如public、private等 |
反射机制的作用?
- 反编译:.class–>.java
- 通过反射机制访问java对象的属性,方法,构造方法等。
2.引入反射机制及其一些实际应用
获取Class类型对象的三种方式:
/**
* 获取Class类对象的三种方式
*
* 获得的Class可以通过newInstance()方法来调用java对象的无参数构造方法。如果确实无参构造方法将报错。
*/
public class ReflectTest01 {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1:
// c1引用保存内存地址指向堆吃对象,该对象代表的是DeadLock整个类。Class.forName(String className); className所需的类的完全限定名
Class c1 = Class.forName("com.cqy.practice.thread.DeadLock");// 将DeadLock.class文件装载到JVM中的过程,静态方法将被执行。
System.out.println(c1);
// 方式2:
// java中每个类中都有class属性,使用.class获取
Class c2 = com.cqy.practice.thread.DeadLock.class;// 不会执行DeadLock中的静态语句块
System.out.println(c2);
// 方式3:
// java语言中任何一个java对象都有getClass()方法
DeadLock d = new DeadLock();// com.cqy.practice.thread.DeadLock
Class c3 = d.getClass();
System.out.println(c3);// c3是运行时类(d的运行时类是DeadLock)
// 因为类在JVM中只有一个,所以c1,c2,c3的内存地址是相同的,指向堆中唯一的一个对象。
System.out.println(c1 == c2);// true
System.out.println(c3 == c2);// true
}
}
IO+Properties的联合应用:
/**
* IO+Properties
* dataBase.properties这样的文件称作配置文件,其作用就是使程序更加灵活
*
* 注意:一般在程序中可变的东西不要写死,推荐写到配置文件中,可以运行相同代码产生不同结果
*
* 属性文件在java规范中要求以“.properties”作为后缀
*
* 属性文件格式要求:
* key和value之间可以使用“空格”、“冒号”、“等号”。
* 如果“空格”、“冒号”、“等号”都有,按最前的作为分隔符
*
* tip:在cmd.exe中native2ascii命令可以将中文转换为Unicode码,属性文件中中文使用Unicode码可以解决乱码的情况
*/
public class IOAndProperties {
public static void main(String[] args) throws IOException {
// 1、创建属性对象
Properties ps = new Properties();// 和Map一样,只不过key和value只能存储字符串类型
// 2、创建输入流
FileInputStream is = new FileInputStream("E:\\learing\\temporary\\dataBase.properties");// username=name
// password=123456
// 将is流中的所有数据加载到属性对象中,其结果是一组组键值对
ps.load(is);
// 关闭流
is.close();
// 通过getProperty(K)方法,使用key值获取value,
System.out.println("username>>>" + ps.getProperty("username"));
System.out.println("password>>>" + ps.getProperty("password"));
}
}
IO+Properties+reflect的联合应用:
// 实体类
class User {
private String id;
public int age;
protected String addr;
boolean sex;
User() {}
User(String id, String addr) {
this.id = id;
this.addr = addr;
}
public String toString() {
return "User["+id+" , "+addr+"]";
}
// 登入
public boolean login(String name, String pwd) {
if ("admin".equals(name) && "123".equals(pwd)) {
return true;
}
return false;
}
// 退出
public void loginOut() {
System.out.println("系统已安全退出!");
}
}
// 综合应用
public class IOAndProtoertiseAndReflect {
public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
// 1、创建属性对象
Properties pro = new Properties();
// 2、创建输入流
FileInputStream fis = new FileInputStream("E:\\learing\\temporary\\IOProRef.properties");// className=com.cqy.practice.reflect.User声明-->
// private String id;public int age;boolean sex;
// 3.加载
pro.load(fis);
// 关闭流
fis.close();
String className = pro.getProperty("className");
System.out.println("className-->" + className);
// 通过反射机制创建对象,通过配置文件使得此处代码更加灵活
Class c = Class.forName(className);
Object o = c.newInstance(); // 创建对象
System.out.println("Object-->" + o);
// 获取public声明的变量名
Field[] fs = c.getFields();
System.out.println(fs[0].getName());// age
// 获取指定的变量,更加灵活的方式对属性赋值,将id放到配置文件中将大大提高配置和获取属性的灵活性
Field fid = c.getDeclaredField("id");
System.out.println(fid.getName());
// 打破封装,可以获取private和protected修饰的变量和方法
fid.setAccessible(true);
fid.set(o, "001");// 给o对象的id赋值001
String id = (String) fid.get(o);// 从o对象中读取id的值
System.out.println("id-->" + id);// 输出:id-->001
// 获取反射机制对应类的相关信息
StringBuffer sb = new StringBuffer();
sb.append(Modifier.toString(c.getModifiers()));// 类名修饰符
sb.append(" class ");
sb.append(c.getSimpleName());// 获取类名
sb.append(" {");
sb.append("\n");// 换行
Field[] fs2 = c.getDeclaredFields();
for (Field f2 : fs2) {
sb.append("\t");// 缩进
sb.append(Modifier.toString(f2.getModifiers()));// 获取修饰符
sb.append(" ");
sb.append(f2.getType().getSimpleName()); // 获取类型
sb.append(" ");
sb.append(f2.getName());// 获取变量名
sb.append(";");
sb.append("\n");// 换行
}
// 获取特定方法,用于方法调用
Method m1 = c.getDeclaredMethod("login", String.class, String.class);// 方法+形参列表,可以将此配置到配置文件中提高灵活性
// 通过o对象的m1方法,传递“admin”、“123”参数,方法的执行结果是returnValue
Object returnValue = m1.invoke(o, "admin", "123");
System.out.println("returnValue-->" + returnValue);// 输出:returnValue-->true
// 获取所有构造方法,反编译参考方法的获取过程
Constructor[] cs = c.getDeclaredConstructors();
// 获取特定的构造方法
Constructor con = c.getDeclaredConstructor(String.class, String.class);
Object obj = con.newInstance("002", "北京清华大学东门公厕内!");
System.out.println("Constructor-->" + obj);
// 获取所有方法
Method[] ms = c.getDeclaredMethods();
for (Method m :ms) {
sb.append("\t");// 缩进
sb.append(Modifier.toString(m.getModifiers()));// 方法修饰符
sb.append(" ");
sb.append(m.getReturnType().getSimpleName());// 返回值类型
sb.append(" ");
sb.append(m.getName());
sb.append("(");
Parameter[] ps = m.getParameters();// 获取所有入参参数
// m.getParameterTypes();// 获取所有类型
int i = 0;
for (Parameter p : ps) {
sb.append(p.getType().getSimpleName());
sb.append(" ");
sb.append(p.getName());
if (i < ps.length - 1) {
sb.append(", ");
}
i++;
}
sb.append(") {\n");
sb.append("\t}\n");
}
sb.append("\n");
sb.append(" }");
System.out.println(sb.toString());
// 通过反射机制获取父类和父接口
Class cStr = Class.forName("java.lang.String");
Class superC = cStr.getSuperclass();
// 输出父类名称
System.out.println(superC.getName());
// 获取父类接口
Class[] classes = cStr.getInterfaces();
for (Class css : classes) {
System.out.println(css.getName());
}
}
}
3.反射机制的两个缺点:
反射机制是一种强大的工具,主要有以下两点不足:
- 性能问题。使用反射机制是一种解释操作,我们告诉JVM,我们希望做什么并且它满足我们的要求。用于字段和方法接入时,反射要远慢于直接代码。性能问题的程度取决于程序中是如何使用反射的。如果它作为程序运行中很少涉及的部分,缓慢的性能将不是一个问题。
- 使用反射会模糊程序内部实际要发生的事情。程序员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术会带来维护问题。反射代码比相应的直接代码更复杂。解决这些问题的最佳方案是保守的使用反射——仅在它可以真正增加灵活性的地方——记录其在目标类中的作用。