程序运行时,允许改变程序结构或变量类型的语言称为动态语言。
java不是动态语言,但他有一个动态相关机制:Reflection反射。反射让java语言活了起来。
在运行状态中:
1)对于任意一个类,可以指定他的所有属性和方法;
2)对于任意一个对象,可以调用他的方法和属性。
总结来说,动态获取信息以及动态调用对象方法的功能成为java反射机制。
查看类信息
每个类被加载后,系统会为该类生成一个对应的Class对象。获取Class对象的三种方式分别是:
public static void main(String[] args) {
//方法一:
try {
Class.forName("Test1"); //必须是该类的完整路径
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//方法二:
Class clazz=Test1.class;
//方法三:调用对象的方法
Test1 test1=new Test1();
test1.getClass();
}
比较:
前两种方式都是直接根据类来取得该类的Class对象,第二种方式更推荐使用。原因有两个:代码更安全,程序性能更好。
下面的例子我们只用第二种方式,第一种很类似,就不再展示了。这里只是测试获取部分类的信息。
//构造的测试类
public class ClassTest {
public ClassTest() {
}
public ClassTest(String name) {
System.out.println("有参数");
}
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "ClassTest [id=" + id + ", name=" + name + "]";
}
public void info() {
System.out.println("执行无参数的info");
}
public void info(String str) {
System.out.println("执行有参数的info" + str);
}
//两个内部类
class Inner {
}
static class Student {
}
//main方法进行测试
public static void main(String[] args) {
// 获取该类的Class
Class<ClassTest> clazz = ClassTest.class;
//返回ClassLoader
System.out.println(clazz.getClassLoader());
// 全部构造器
System.out.println("ClassTest的构造器" + "-----------------");
Constructor[] ctors = clazz.getDeclaredConstructors();
for (Constructor c : ctors) {
System.out.println(c);
}
System.out.println();
System.out.println("全部public方法"+"------------");
Method[] meths = clazz.getMethods();
for (Method c : meths) {
System.out.println(c);
}
//加载内部类
Class<?>[] inners=clazz.getDeclaredClasses();
System.out.println("全部内部类如下:"+"----------");
for(Class c:inners){
System.out.println(c);
}
}
}
执行结果如下:
操作对象
Class对象可以操作类中的方法、构造器、变量等。
继续测试上面的例子
// 操作对象
System.out.println("操作对象-------");
try {
Class<?> class1 = Class.forName("ClassTest");
ClassTest test = (ClassTest) class1.newInstance();
System.out.println("调用方法-------");
test.info();
test.setId(1);
test.setName("Sherry");
System.out.println("增加对象:"+test.toString());
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException e) {
e.printStackTrace();
}
那些年,我们悄悄用过的
1、机房收费
第一次真正接触反射是在做机房收费系统的时候,当时使用的是抽象工厂+反射或者反射+配置文件。回到当年看大话的年代……
需求:能不能不换DB?
有没有感觉上面的写法其实跟java中的Class.forName差不多的,毕竟原理是一样的嘛。将程序集、命名空间、类名传入Reflection中,这样做使得配置更加灵活,不需要在写一堆if else,搭配配置文件就更好了。
2、抽取dao
在ssh架构中,将增删改查等方法抽取出来做成一个泛型类,运行时动态的传入具体的类。
抽取的方式:
1)通过类型
// 通过反射获取具体对象
private Class<T> classz;
//由构造函数中确定class
public DaoSupportImpl() {
// 获取当前new对象的泛型的父类的类型
ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
this.classz = (Class<T>) pt.getActualTypeArguments()[0]; // 获取第一个类型参数的真实类型
System.out.println(classz);
}
确定了类之后,CRUD什么的就简单多了,来个例子就好。
//查询所有
@Override
public List<T> findAll() {
System.out.println(classz.getSimpleName());
return getSession().createQuery( //
"FROM " + classz.getSimpleName())//
.list();
}
2)通过类名
//通过构造器注入类名
public DaoSupportImpl(String className) {
this.className = className;
}
// 根据id 查询
public T findById(Serializable id) {
Class c = null;
try {
c = Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T) this.getHibernateTemplate().get(c, id);
}