反射
反射:在类运行时,能够动态获取并访问其构造结构的机制称为反射机制。
反射是Java中框架设计的核心,通过对类的构造、属性、方法等数据的获取提供抽象的底层构建
反射机制:
反射需要先获得类的class字节码,由JVM类加载器(ClassLoader)负责加载,并在内存中缓存class的内部结构。借助于Java的反射机制允许快速对类的结构数据进行访问和调用
获得class字节码文件的三种方式:
1.Object对象的getClass()方法
获得方式一
Class<User> userClass = (Class<User>) user.getClass();
2.类的class静态属性(一个类只有一个class,实例化多次对应的都是同一个class,所以class可以用==进行比较,a.getClass==b.getClass)
获得方式二 Class<User> userClass = User.class;
3.Class.forName(String classPath)的加载(反射应用的核心)
获得方式三 通过类路径访问类的Class信息,在不知道类型的情况下,可以不写泛型
Class<User> userClass = (Class<User>) Class.forName("com.igeek.pojo.User");
反射允许获取到类的各种构造结构,包括构造方法、方法、属性、接口、注解等
反射能够对类完成实例构建,并能对方法进行实例调用
第三种方法的使用:
Class objClass = Class.forName(“com.igeek.pojo.User”)
Object obj = objClass.newInstance()
获取类的构造方法
getConstructor(Class<T>… parameterTypes)
getConstructors()
获取类的属性
getField(String fieldName)
getFields()
获取类的方法
getMethod(String methodName,Class<T>…parameterType)
getMethods()
获取类的接口
getInterfaces()
获取类的超类
getSuperclass()
XML
Extensible Markup Language 可扩展标记语言
作用:
1.用于对工程进行外部配置
2.用于描述复杂格式的数据,对数据进行存储管理
3.用于跨应用进行数据的交互
每一个XML文件都以标签的方式进行编写,并且必须要有一个跟标签
XML中每个标签都称为节点或元素,标签体的文本也是节点元素,称为文本节点
定义根标签:使用DTD或者是Schema文件来定义XML的内容
解析XML文档主要有两种方式:
1.DOM解析
特点: 将整个XML文档全部加载至内存中,在内存中对文档进行解析
2.SAX解析
特点:使用事件驱动方式进行解析,一边读取一边解析
DOM解析的使用步骤:
1.DOM4j架包导入:
File——Project Structures——Libraries——选择DOM所在的位置——OK
2.创建XML
Setting——Editor——File and Code templates——"+"——名称为“new.xml”——输入“<?xml version="1.0" encoding="UTF-8"?>”——单击"OK"
3.新建文件
新建文件进行new一个new.xml文件
MyInterfaceA
package com.igeek; public interface MyInterfaceA { }
MyInterfaceB
package com.igeek; public interface MyInterfaceB { }
Human类
package com.igeek.pojo; public class Human { public int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
User类
package com.igeek.pojo; import com.igeek.MyInterfaceA; import com.igeek.MyInterfaceB; //实现了A、B两个接口,继承了Human类 public class User extends Human implements MyInterfaceA, MyInterfaceB { //三个属性 private int userId; private String userName; public String password; //空参构造方法 public User() { } //带参构造方法 public User(int userId, String userName, String password) { this.userId = userId; this.userName = userName; this.password = password; } //属性的get 、set方法 public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
获取类的方法
package com.igeek; import com.igeek.pojo.Human; import com.igeek.pojo.User; /** * 获得class文件 * 反射是基于Class类型访问的 * Class对象封装缓存了类的数据结构 */ public class Demo1 { public static void main(String[] args) { User user = new User(); Human human = new User(); //获得方式一:返回值为Class,不写泛型默认为object类型,写了泛型就要进行强转 //将类实例化以后对对象进行获取class处理 Class<User> userClass = (Class<User>) user.getClass(); //获得方式二:不用对类进行实例化,直接对类进行获取class处理 Class<User> userClass = User.class; //获得方式三 通过类路径访问类的Class信息 try { //这个方法是因为:这个方法是可以在不明确类型和其结构时使用,
(第一个方法都已经将对象实例化出来了,不需要使用反射。) Class<User> userClass = (Class<User>) Class.forName("com.igeek.pojo.User"); } catch (ClassNotFoundException e) { e.printStackTrace(); } //上面两种(第一种和第二种获取class方法)方式主要用于多态场景下的类型匹配
if(human instanceof User){ System.out.println("human的实例类型是user类型"); } if(human.getClass() == User.class){ System.out.println("human的实例类型是user类型"); } } }
第三种获取class方法的应用:
package com.igeek; import com.igeek.pojo.User; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * 使用反射解析类的数据结构 */ public class Demo2 { public static void main(String[] args) { try { //获得类的Class对象(第三种方法),该对象封装了类的数据结构信息 Class objClass = Class.forName("com.igeek.pojo.User"); //使用反射将类进行实例化 //该方式只能通过类的无参构造进行实例化 Object obj = objClass.newInstance(); System.out.println("实例化后的结果:"+obj); //获得类的所有的构造方法:返回值是构造方法的数组 构造方法的类型是Constructor类型 Constructor[] constructors = objClass.getConstructors();
//可获得私有的构造方法
Constructor[] constructors=ObjClass.getDeclareConstructors(); System.out.println("该类有"+constructors.length+"个构造方法"); //遍历所有的构造 for (Constructor constructor : constructors) { System.out.println("\n-----------------------------"); //获得构造方法的参数数量 int paramCount = constructor.getParameterCount(); System.out.println("构造方法的参数数量:"+paramCount); //获得构造方法的参数类型 Class[] paramClassArray = constructor.getParameterTypes(); for (Class paramClass : paramClassArray) { System.out.print(paramClass.getName()+"\t"); } } System.out.println(); //根据参数获得指定的构造方法 //获得无参的构造 Constructor constructor1 = objClass.getConstructor(); System.out.println(constructor1); //根据构造方法来实例化对象 Object obj1 = constructor1.newInstance(); System.out.println(obj1); //获得带参构造方法:通过参数的class获取指定的构造方法
//基本类型和包装类的class是不同的 Constructor constructor2 = objClass.getConstructor(int.class,String.class,String.class); //使用带参的构造方法实例化对象
//可以用Object接收
Object obj2=constructor2.newInstance(100,"tom","123");
//也可强转后用User接收 User user = (User) constructor2.newInstance(100,"tom","123"); System.out.println(user.getUserId()+"\t"+user.getUserName()+"\t"+user.getPassword()); //获得当前类的父类 Class superClass = objClass.getSuperclass(); System.out.println("\n------------父类------------");
//若结果是Object则这个类没有手动进行extends继承 System.out.println(superClass); //获取实现的接口 Class[] interfaceClasses = objClass.getInterfaces(); System.out.println("\n---------实现的接口---------"); for (Class interfaceClass : interfaceClasses) { System.out.println(interfaceClass.getName()); }
//获得属性 //访问当前允许访问(具有访问权限)到的属性,包括父类继承得到的属性
(不同包中加了protect或者加了private或者没加访问修饰符的都没有访问权限) Field[] fields = objClass.getFields(); //访问当前类(本类)直接声明的属性,不论访问修饰符是什么 Field[] fields = objClass.getDeclaredFields(); System.out.println("\n------------属性-------------"); for (Field field : fields) { //获得属性的名称 System.out.println("属性名称:"+field.getName()); //获得属性的类型 System.out.println("属性类型:"+field.getType()); } System.out.println("\n-------------------------------"); //获得指定名称属性 Field nameField = objClass.getDeclaredField("userName"); //为属性提供访问权限,即使是私有属性也将允许访问处理 nameField.setAccessible(true); //对属性进行赋值,第一个参数表示该属性从属的对象,第二个参数表示要设置的值 //set方法允许对属性进行赋值,但是会根据属性本身的访问修饰符来决定是否可以成功操作
(paivate时不能被set,但是setAccessible后即可不管是什么访问修饰符都可以进行设置可以访问处理) nameField.set(user,"jack"); //System.out.println(user.getUserName()); //获取属性的值 Object value = nameField.get(user); System.out.println(value); System.out.println("--------------方法的访问-------------");
//获取所有方法,包括私有的
Method[] methods=objClass.getDeclareMethods();
//获取所有方法 Method[] methods = objClass.getMethods(); for (Method method : methods) { //获取方法的名称 System.out.println("方法名:"+method.getName()); //获取返回值类型 Class returnClass = method.getReturnType(); System.out.println("返回值类型:"+returnClass); //获取方法的参数类型 Class[] paramClassArray = method.getParameterTypes(); System.out.println("方法的参数:"); for (Class paramClass : paramClassArray) { System.out.println(paramClass); } System.out.println("------------------"); } //获取一个指定名称的方法:第一个参数是方法的名称,第二个参数是参数的类型,可以多个可以一个可以没有 Method setMethod = objClass.getMethod("setUserName", String.class); //使用反射调用方法(修改原内容):第一个参数是代表修改的哪个对象中的内容,第二个参数是要修改成的值 setMethod.invoke(user, "admin"); Method getMethod = objClass.getMethod("getUserName"); //调用方法(获得返回值):返回值是Object类型,如果调用的方法没有返回值,则返回的值null Object returnValue = getMethod.invoke(user); System.out.println("返回值是:"+returnValue); } catch (Exception e) { e.printStackTrace(); } } }
例子:
使用反射动态解析javax.swing.JFrame类
使用反射实例化该对象
使用反射动态解析该类的构造方法,获得构造方法的参数数量和类型
并使用无参构造以及带有String的构造
方法来实例化JFrame对象
package Demo1; import java.lang.reflect.Constructor; public class Demo { public static void main(String[] args) { //获取类方法 try { Object objClass=Class.forName("javax.swing.JFrame"); //使用反射实例化该对象 Object obj=((Class) objClass).newInstance(); System.out.println("实例化后的对象是:"+obj); //使用反射动态解析该类的构造方法,获得构造方法的参数数量和类型 Constructor[] constructors=((Class) objClass).getConstructors(); System.out.println("共有"+constructors.length+"个构造方法"); //获得构造方法的参数数量和类型 for (Constructor constructor : constructors) { //获取参数数量 int count=constructor.getParameterCount(); System.out.println("该构造方法有"+count+"个参数"); //获取参数类型 Class[] classes=constructor.getParameterTypes(); for (Class aClass : classes) { System.out.println(aClass.getName()+"\t"); } } //使用无参构造 Constructor con1=((Class) objClass).getConstructor(); Object Obj1=con1.newInstance(); System.out.println("无参构造实例化对象是:"+Obj1); //带有String的构造 Constructor con2=((Class) objClass).getConstructor(String.class); Object Obj2=con2.newInstance("我是一个标题"); System.out.println("带参构造实例化对象是:"+Obj2);
若是想要获得父类和子类所以的属性,则需要进行递归,先对子类进行属性的获得,然后获得子类的父类,如果不是Object类型,则使用递归将父类传入继续进行属性的获得
XML的使用:
根标签:(user是标签名称, id是标签属性, 1001是标签值, userName和password是标签体的内容)
XML中每个标签都称为节点或元素,标签体的文本也是节点元素,称为文本节点
(紫色标记的都是节点,红色标记的都是文本加点,所以一共11个节点)
框架中灵活处理的内容都要写成配置文件,通过底层代码对配置文件进行解析,解析完成后通过反射进行处理调用
<?xml version="1.0" encoding="UTF-8"?>
//相当于集合存储用户信息 <users> //用户一 <user id="1001" age=20> //标签体(标签内容) <userName>tom</userName> <password>tom123</password> </user> //标签二:用户二 <user id="1002" age=21> //标签体(标签内容) <userName>jack</userName> <password>jack123</password> </user> </users>
package com.igeek; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import java.util.List; /** * 使用DOM4J对XML文档进行解析 */ public class Demo3 { public static void main(String[] args) { //使用ClassLoader获取资源是在类路径(src文件夹内)中访问的 //System.out.println(Demo3.class.getClassLoader().getResource("").getPath()); //使用类自身Class对象获取资源是在类所在的包路径中访问的 //System.out.println(Demo3.class.getResource("").getPath()); //从类路径中访问文件并将文件转换为输入流 //System.out.println(Demo3.class.getClassLoader().getResourceAsStream("test.xml")); try { //创建SAX解析器对象 SAXReader reader = new SAXReader(); //通过获取xml文件的输入流,加载文档
//通过类的class获取classLoader ,在通过classLoader读取文件的输入流
//这个方式是从当前类所在的包中查询test.xml文件
//Document document=reader.read(Dome3.class.getClass().getResourceAsStream("test.xml"));
//这个方式是从整个项目的路径中查询test.xml文件 Document document = reader.read(Demo3.class.getClassLoader().getResourceAsStream("test.xml")); //获取文档的根节点对象 Element root = document.getRootElement(); //访问节点的名字 System.out.println(root.getName()); //访问节点中的所有下一层子节点 List<Element> list = root.elements();//获取的是数组:所有的节点 //遍历user标签 for (Element element : list) { System.out.println(element.getName()); //访问标签的所有属性,也可以通过attribute通过名称获取单独的一个属性 List<Attribute> attributeList = element.attributes();//得到的是集合所有的属性 //遍历属性(属性的类型是:Attibute标签的类型是:Element) for (Attribute attribute : attributeList) { //获取属性的名称 System.out.print("\t"+attribute.getName()+":"); //获取属性的值 System.out.println(" "+attribute.getValue()); } //获得指定的userName子标签 Element nameNode = element.element("userName"); System.out.println("\t"+nameNode.getName()); //获取userName的标签体文本:trim是去除空格(如果只是需要查找不需要操作,也可以不用接收直接打印) String name = nameNode.getTextTrim(); System.out.println("\t\t"+name); //获取password子标签 Element pwdNode = element.element("password"); System.out.println("\t"+pwdNode.getName()); System.out.println("\t\t"+pwdNode.getTextTrim()); } } catch (DocumentException e) { e.printStackTrace(); } } }