本期精彩:
目录
上期回顾:手把手教会:XML建模_小阿飞_的博客-CSDN博客
这是上期文章需要解析的config.xml文件👇
通过观察我们发现,如果画红圈处的 “ / ” 没有的话,在解析文件时如果传入一个没有“ / ” 的path,显然是会被解析成功的,但是config.xml文件中path的格式必须以“ / ”开头,所以我们要做的优化就是:让没有“ / ”的path在config.xml在运行时第一个报错
具体操作:发现action和forward中都有path,则在相对应的模型类中加上类似如下的代码即可👇
private static final long serialVersionUID = 4907025244685971932L;
private static Pattern pattern = Pattern.compile("^/.+$");
public void checkPath(String path) {
Matcher matcher = pattern.matcher(path);
boolean b = matcher.matches();
if (!b) {
throw new RuntimeException("ForwardModel.path[" + path + "]必须以/开头");
}
}
本期内容:
反射的概念
概念
反射机制是在运行时,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意个对象,都能够调用它的任意一个方法
在java中,只要给定类的名字,就可以通过反射机制来获得类的所有信息
反射是Java语言中的一种机制,通过这种机制可以动态地实例化对象、读写属性、调用方法
反射也是很多框架开发的基础,比如hibernate,struts等框架
jdbc就是典型的反射👇(还有XML文件中也存在反射)
Class.forName('com.mysql.jdbc.Driver.class');//加载MySQL的驱动类
反射的优缺点
优点:
能够运行时动态获取类的实例,提高灵活性;
与动态编译结合;
缺点:
使用反射性能较低,需要解析字节码(.class文件),将内存中的对象进行解析;
相对不安全,破坏了封装性(因为通过反射可以获得私有方法和属性);
解决方案:
1、通过setAccessible(true)关闭JDK的安全检查来提升反射速度;
2、多次创建一个类的实例时,有缓存会快很多;
3、ReflectASM工具类,通过字节码生成的方式加快反射速度 ;
Class类
概念
Java中,每个class都有一个相应的Class对象
也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息
理解:我们在Eclipse中创建的类( .Java )会被编译成.class文件给JVM读取,当.class文件被读到内存中后就会变成Class模型(中有之前编写的公有或者私有的属性、方法、构造函数等),通过Class就可以点出里面的对象
获取Class对象的方法
后面的代码都以下面这个学生类( .Java )为例子👇
public class Student {
private String sid;
private String sname;
public Integer age;
static{
System.out.println("加载进jvm中!");
}
public Student() {
super();
System.out.println("调用无参构造方法创建了一个学生对象");
}
public Student(String sid) {
super();
this.sid = sid;
System.out.println("调用带一个参数的构造方法创建了一个学生对象");
}
public Student(String sid, String sname) {
super();
this.sid = sid;
this.sname = sname;
System.out.println("调用带二个参数的构造方法创建了一个学生对象");
}
@SuppressWarnings("unused")
private Student(Integer age) {
System.out.println("调用Student类私有的构造方法创建一个学生对象");
this.age = age;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public void hello() {
System.out.println("你好!我是" + this.sname);
}
public void hello(String name) {
System.out.println(name + "你好!我是" + this.sname);
}
@SuppressWarnings({ "unused", "deprecation" })
private Integer add(Integer a, Integer b) {
return new Integer(a.intValue() + b.intValue());
}
@Override
public String toString() {
return "Student [sid=" + sid + ", sname=" + sname + ", age=" + age + "]";
}
}
获取这个学生类的Class对象的三种方法👇
//1.Class.forName("类的权限命名") 获取Class
Class<Student> clazz =(Class<Student>)Class.forName("com.reflect.Student");
//2. 类.class
Class clazz02 = Student.class;
//3. 对象.getClass()
Student stu = new Student();
Class clazz03 = stu.getClass();
补充:如何获取类的权限命名👇
先找到
再右键
反射实例化对象
还是以上面的学生类为例,通过反射进行实例化学生对象的方式有👇
其步骤是先通过学生类的Class对象点出其构造方法,再通过构造方法点出实例化对象的方法
补充:出于安全考虑,在通过反射获取方法、属性时可以使用 Declared 获取
//获取无参构造,Constructor:构造函数
Constructor<Student> c1 = clazz.getConstructor();
//实例学生对象
Student stu01 = c1.newInstance();
stu01.setSname("小明");
System.out.println(stu01);
//获取有参构造
Constructor<Student> c2 = clazz.getConstructor(String.class);
Student stu02 = c2.newInstance("1");
stu02.setSname("小黑");
System.out.println(stu02);
//获取有参构造
Constructor<Student> c3 = clazz.getConstructor(String.class,String.class);
Student stu03 = c3.newInstance("2", "小哈");
System.out.println(stu03);
//获取私有化的有参构造:Declared
Constructor<Student> c4 = clazz.getDeclaredConstructor(Integer.class);
//允许访问,Accessible访问控制符
c4.setAccessible(true);
Student stu04 = c4.newInstance(22);
System.out.println(stu04);
补充:反射获取构造函数中判断参数个数的方法:使用可变参数(如果前面还有参数,必须放在最后),查看clazz.getConstructor源码即可发现:
反射调用方法
通过反射获取上面学生类中方法的代码👇
Method method = clazz.getMethod("hello");
stu04.setSname("lihao");
//调用方法
method.invoke(stu04);
Method method02 = clazz.getMethod("hello",String.class);
method02.invoke(stu04, "zengfanyan");
//获取私有方法
Method method03 = clazz.getDeclaredMethod("add",Integer.class, Integer.class);
method03.setAccessible(true);
int rv = (int)method03.invoke(stu04, 1,1);
System.out.println(rv);
反射读写属性
通过反射获取上面学生类中属性的代码👇
Field f = clazz.getField("age");
f.set(stu04, 78);
System.out.println(stu04);
System.out.println(f.get(stu04));
//获取私有属性
Field f01 = clazz.getDeclaredField("sname");
f01.setAccessible(true);
//调用属性
f01.set(stu04, "lihao is hao ren");
System.out.println(stu04);
补充:
Bean对象除了上述获取属性的方法之外,还可以通过BeanUtil.getproperty(对象名,“属性”)的方式获取属性,其原理是先调用类中的公开get属性的方法获取再通过反射机制进行获取
👆和JavaBean的封装的设计有关:一个对象类中私有属性,公开get(得到值)、set(注入值)方法的意义是提供统一控制获取和处理类中私有属性的方法入口