反射:在类运行时,能够动态获取并访问其构造结构的机制称为反射机制。
反射是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();
}
}
}